Skip to content

Commit b46a5f9

Browse files
authored
Merge pull request serilog#291 from skomis-mm/staticMemberAccess
static member access for parameters of concrete types
2 parents 9b3c754 + 325b711 commit b46a5f9

File tree

4 files changed

+69
-22
lines changed

4 files changed

+69
-22
lines changed

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
3.3.1
4+
5+
* #287 - Fix static member access for concrete type parameters
6+
37
3.3.0
48

59
* #276, #225, #167 - added support for constructors with arguments for complex types

src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,12 @@ public object ConvertTo(Type toType, ResolutionContext resolutionContext)
6363
if (convertor != null)
6464
return convertor(argumentValue);
6565

66-
if ((toTypeInfo.IsInterface || toTypeInfo.IsAbstract) && !string.IsNullOrWhiteSpace(argumentValue))
66+
if (!string.IsNullOrWhiteSpace(argumentValue))
6767
{
68-
//check if value looks like a static property or field directive
68+
// check if value looks like a static property or field directive
6969
// like "Namespace.TypeName::StaticProperty, AssemblyName"
70-
if (TryParseStaticMemberAccessor(argumentValue, out var accessorTypeName, out var memberName))
70+
if (toType != typeof(string) &&
71+
TryParseStaticMemberAccessor(argumentValue, out var accessorTypeName, out var memberName))
7172
{
7273
var accessorType = Type.GetType(accessorTypeName, throwOnError: true);
7374
// is there a public static property with that name ?
@@ -96,25 +97,28 @@ public object ConvertTo(Type toType, ResolutionContext resolutionContext)
9697
throw new InvalidOperationException($"Could not find a public static property or field with name `{memberName}` on type `{accessorTypeName}`");
9798
}
9899

99-
// maybe it's the assembly-qualified type name of a concrete implementation
100-
// with a default constructor
101-
var type = FindType(argumentValue.Trim());
102-
if (type == null)
100+
if (toTypeInfo.IsInterface || toTypeInfo.IsAbstract)
103101
{
104-
throw new InvalidOperationException($"Type {argumentValue} was not found.");
105-
}
102+
// maybe it's the assembly-qualified type name of a concrete implementation
103+
// with a default constructor
104+
var type = FindType(argumentValue.Trim());
105+
if (type == null)
106+
{
107+
throw new InvalidOperationException($"Type {argumentValue} was not found.");
108+
}
106109

107-
var ctor = type.GetTypeInfo().DeclaredConstructors.Where(ci => !ci.IsStatic).FirstOrDefault(ci =>
108-
{
109-
var parameters = ci.GetParameters();
110-
return parameters.Length == 0 || parameters.All(pi => pi.HasDefaultValue);
111-
});
110+
var ctor = type.GetTypeInfo().DeclaredConstructors.Where(ci => !ci.IsStatic).FirstOrDefault(ci =>
111+
{
112+
var parameters = ci.GetParameters();
113+
return parameters.Length == 0 || parameters.All(pi => pi.HasDefaultValue);
114+
});
112115

113-
if (ctor == null)
114-
throw new InvalidOperationException($"A default constructor was not found on {type.FullName}.");
116+
if (ctor == null)
117+
throw new InvalidOperationException($"A default constructor was not found on {type.FullName}.");
115118

116-
var call = ctor.GetParameters().Select(pi => pi.DefaultValue).ToArray();
117-
return ctor.Invoke(call);
119+
var call = ctor.GetParameters().Select(pi => pi.DefaultValue).ToArray();
120+
return ctor.Invoke(call);
121+
}
118122
}
119123

120124
return Convert.ChangeType(argumentValue, toType);

test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,16 +89,42 @@ public void FindTypeSupportsSimpleNamesForSerilogTypes(string input, Type target
8989
[InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::AbstractProperty, Serilog.Settings.Configuration.Tests", typeof(AnAbstractClass))]
9090
[InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::InterfaceField, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))]
9191
[InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::AbstractField, Serilog.Settings.Configuration.Tests", typeof(AnAbstractClass))]
92-
public void StaticMembersAccessorsCanBeUsedForReferenceTypes(string input, Type targetType)
92+
public void StaticMembersAccessorsCanBeUsedForAbstractTypes(string input, Type targetType)
9393
{
94-
var stringArgumentValue = new StringArgumentValue($"{input}");
94+
var stringArgumentValue = new StringArgumentValue(input);
9595

9696
var actual = stringArgumentValue.ConvertTo(targetType, new ResolutionContext());
9797

9898
Assert.IsAssignableFrom(targetType, actual);
9999
Assert.Equal(ConcreteImpl.Instance, actual);
100100
}
101101

102+
[Theory]
103+
[InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::ConcreteClassProperty, Serilog.Settings.Configuration.Tests", typeof(AConcreteClass))]
104+
[InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::ConcreteClassField, Serilog.Settings.Configuration.Tests", typeof(AConcreteClass))]
105+
public void StaticMembersAccessorsCanBeUsedForConcreteReferenceTypes(string input, Type targetType)
106+
{
107+
var stringArgumentValue = new StringArgumentValue(input);
108+
109+
var actual = stringArgumentValue.ConvertTo(targetType, new ResolutionContext());
110+
111+
Assert.IsAssignableFrom(targetType, actual);
112+
Assert.Equal(ConcreteImplOfConcreteClass.Instance, actual);
113+
}
114+
115+
[Theory]
116+
[InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::IntProperty, Serilog.Settings.Configuration.Tests", typeof(int), 42)]
117+
[InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::StringProperty, Serilog.Settings.Configuration.Tests", typeof(string),
118+
"Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::StringProperty, Serilog.Settings.Configuration.Tests")]
119+
public void StaticMembersAccessorsCanBeUsedForBuiltInTypes(string input, Type targetType, object expected)
120+
{
121+
var stringArgumentValue = new StringArgumentValue(input);
122+
123+
var actual = stringArgumentValue.ConvertTo(targetType, new ResolutionContext());
124+
125+
Assert.Equal(expected, actual);
126+
}
127+
102128
[Theory]
103129
// unknown type
104130
[InlineData("Namespace.ThisIsNotAKnownType::InterfaceProperty, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))]

test/Serilog.Settings.Configuration.Tests/Support/StaticAccessorClasses.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public abstract class AnAbstractClass
88
{
99
}
1010

11-
internal class ConcreteImpl : AnAbstractClass, IAmAnInterface
11+
class ConcreteImpl : AnAbstractClass, IAmAnInterface
1212
{
1313
private ConcreteImpl()
1414
{
@@ -17,13 +17,26 @@ private ConcreteImpl()
1717
public static ConcreteImpl Instance { get; } = new ConcreteImpl();
1818
}
1919

20+
public class AConcreteClass
21+
{
22+
}
23+
24+
class ConcreteImplOfConcreteClass : AConcreteClass
25+
{
26+
public static ConcreteImplOfConcreteClass Instance { get; } = new ConcreteImplOfConcreteClass();
27+
}
28+
2029
public class ClassWithStaticAccessors
2130
{
2231
public static IAmAnInterface InterfaceProperty => ConcreteImpl.Instance;
2332
public static AnAbstractClass AbstractProperty => ConcreteImpl.Instance;
33+
public static AConcreteClass ConcreteClassProperty => ConcreteImplOfConcreteClass.Instance;
34+
public static int IntProperty => 42;
35+
public static string StringProperty => "don't see me";
2436

2537
public static IAmAnInterface InterfaceField = ConcreteImpl.Instance;
2638
public static AnAbstractClass AbstractField = ConcreteImpl.Instance;
39+
public static AConcreteClass ConcreteClassField = ConcreteImplOfConcreteClass.Instance;
2740

2841
// ReSharper disable once UnusedMember.Local
2942
private static IAmAnInterface PrivateInterfaceProperty => ConcreteImpl.Instance;
@@ -34,4 +47,4 @@ public class ClassWithStaticAccessors
3447
public IAmAnInterface InstanceInterfaceProperty => ConcreteImpl.Instance;
3548
public IAmAnInterface InstanceInterfaceField = ConcreteImpl.Instance;
3649
}
37-
}
50+
}

0 commit comments

Comments
 (0)