Skip to content

Commit ab05cc0

Browse files
Fix setting property with private setter on base type (#390)
* Fix setting property with private setter on base type * Small refactoring: Breakup LINQ statement for debugability and better readability. Co-authored-by: Thomas Levesque <[email protected]> Co-authored-by: bchavez <[email protected]>
1 parent e8c1f45 commit ab05cc0

File tree

2 files changed

+76
-4
lines changed

2 files changed

+76
-4
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using FluentAssertions;
2+
using Xunit;
3+
4+
namespace Bogus.Tests.GitHubIssues
5+
{
6+
public class Issue389
7+
{
8+
[Fact]
9+
public void property_with_private_setter_in_base_class_is_assigned()
10+
{
11+
var foo = new Faker<Foo>()
12+
.RuleFor(f => f.PropInFoo, _ => 42)
13+
.RuleFor(f => f.PropInFooBase, _ => 123)
14+
.Generate();
15+
16+
foo.PropInFoo.Should().Be(42);
17+
foo.PropInFooBase.Should().Be(123);
18+
}
19+
20+
public class FooBase
21+
{
22+
public int PropInFooBase { get; private set; }
23+
}
24+
25+
public class Foo : FooBase
26+
{
27+
public int PropInFoo { get; private set; }
28+
}
29+
30+
public class Zoo : Foo
31+
{
32+
public int PropInZoo { get; private set; }
33+
}
34+
35+
[Fact]
36+
public void property_with_private_setter_inheritance_chain_is_assigned()
37+
{
38+
var zoo = new Faker<Zoo>()
39+
.RuleFor(f => f.PropInFoo, _ => 42)
40+
.RuleFor(f => f.PropInFooBase, _ => 123)
41+
.RuleFor(f => f.PropInZoo, _ => 77)
42+
.Generate();
43+
44+
zoo.PropInFoo.Should().Be(42);
45+
zoo.PropInFooBase.Should().Be(123);
46+
zoo.PropInZoo.Should().Be(77);
47+
}
48+
}
49+
}

Source/Bogus/Binder.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public Binder(BindingFlags bindingFlags)
4949
{
5050
BindingFlags = bindingFlags;
5151
}
52+
5253

5354
/// <summary>
5455
/// Given T, the method will return a Dictionary[string,MemberInfo] where
@@ -60,27 +61,34 @@ public Binder(BindingFlags bindingFlags)
6061
/// <returns>The full set of MemberInfos for injection.</returns>
6162
public virtual Dictionary<string, MemberInfo> GetMembers(Type t)
6263
{
63-
var group = t.GetAllMembers(BindingFlags)
64+
var allReflectedMembers = t.GetAllMembers(this.BindingFlags)
65+
.Select(m => UseBaseTypeDeclaredPropertyInfo(t, m));
66+
67+
var settableMembers = allReflectedMembers
6468
.Where(m =>
6569
{
6670
if( m.GetCustomAttributes(typeof(CompilerGeneratedAttribute), true).Any() )
6771
{
6872
//no compiler generated stuff
6973
return false;
7074
}
75+
7176
if( m is PropertyInfo pi )
7277
{
7378
return pi.CanWrite;
7479
}
80+
7581
if( m is FieldInfo fi )
7682
{
7783
//No private fields.
7884
//GitHub Issue #13
7985
return !fi.IsPrivate;
8086
}
87+
8188
return false;
82-
})
83-
.GroupBy(mi => mi.Name);
89+
});
90+
91+
var settableMembersByName = settableMembers.GroupBy(mi => mi.Name);
8492

8593
//Issue #70 we could get back multiple keys
8694
//when reflecting over a type. Consider:
@@ -92,7 +100,22 @@ public virtual Dictionary<string, MemberInfo> GetMembers(Type t)
92100
//reflected MemberInfo that was returned from
93101
//reflection; the second one was the inherited
94102
//ClassA.Value.
95-
return group.ToDictionary(k => k.Key, g => g.First());
103+
return settableMembersByName.ToDictionary(k => k.Key, g => g.First());
104+
}
105+
106+
//Issue #389 - Use Declaring Base Type PropertyInfo instead of a DerivedA's
107+
//PropertyInfo because DerivedA's PropertyInfo could say property is not
108+
//write-able.
109+
protected virtual MemberInfo UseBaseTypeDeclaredPropertyInfo(Type t, MemberInfo m)
110+
{
111+
if( m is PropertyInfo {CanWrite: false} && m.DeclaringType is not null && m.DeclaringType != t )
112+
{
113+
var newPropInfo = m.DeclaringType.GetProperty(m.Name, this.BindingFlags);
114+
if( newPropInfo is not null )
115+
return newPropInfo;
116+
}
117+
118+
return m;
96119
}
97120
}
98121
}

0 commit comments

Comments
 (0)