Skip to content

Commit 3f9c009

Browse files
committed
initial required properties support and test for #563
1 parent 89cc161 commit 3f9c009

File tree

3 files changed

+109
-4
lines changed

3 files changed

+109
-4
lines changed

src/DryIoc/Container.cs

+51-4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ THE SOFTWARE.
2929
#if !NET45 && !NET451 && !NET452 && !NET46 && !NET461 && !NET462 && !NET47
3030
#define SUPPORTS_EXPRESSION_COMPILE_WITH_PREFER_INTERPRETATION_PARAM
3131
#endif
32+
#if NET7_0_OR_GREATER
33+
#define SUPPORTS_REQUIRED_PROPERTIES
34+
#endif
3235

3336
namespace DryIoc
3437
{
@@ -9271,10 +9274,19 @@ public PropertyOrFieldServiceInfo(Type serviceType) : base(serviceType) { }
92719274
/// <param name="holder">Holder of property or field.</param> <param name="value">Value to set.</param>
92729275
public abstract void SetValue(object holder, object value);
92739276

9274-
/// <summary>Create member info out of provide property or field.</summary>
9277+
/// <summary>Create property or field service info out of provided member.</summary>
92759278
public static PropertyOrFieldServiceInfo Of(MemberInfo member) =>
92769279
member.ThrowIfNull() is PropertyInfo ? new Property((PropertyInfo)member) : (PropertyOrFieldServiceInfo)new Field((FieldInfo)member);
92779280

9281+
/// <summary>Create property service info out of provided property.</summary>
9282+
public static PropertyOrFieldServiceInfo Of(PropertyInfo property) => new Property(property);
9283+
9284+
/// <summary>Create property service info out of provided property with the details of `IfUnresolved.Throw`.</summary>
9285+
public static PropertyOrFieldServiceInfo OfRequiredProperty(PropertyInfo property) => new Property.RequiredProperty(property);
9286+
9287+
/// <summary>Create field service info out of provided property.</summary>
9288+
public static PropertyOrFieldServiceInfo Of(FieldInfo field) => new Field(field);
9289+
92789290
private class Property : PropertyOrFieldServiceInfo
92799291
{
92809292
public override MemberInfo Member => _property;
@@ -9297,6 +9309,12 @@ private sealed class WithDetails : Property
92979309
public WithDetails(PropertyInfo property, ServiceDetails details) : base(property) => Details = details;
92989310
public WithDetails(PropertyInfo property, Type serviceType, ServiceDetails details) : base(property, serviceType) => Details = details;
92999311
}
9312+
9313+
internal sealed class RequiredProperty : Property
9314+
{
9315+
public override ServiceDetails Details => ServiceDetails.Default; // with IfUnresolved.Throw
9316+
public RequiredProperty(PropertyInfo property) : base(property) {}
9317+
}
93009318
}
93019319

93029320
private class Field : PropertyOrFieldServiceInfo
@@ -11276,6 +11294,35 @@ public static PropertiesAndFieldsSelector Properties(
1127611294
IfUnresolved ifUnresolved = IfUnresolved.ReturnDefaultIfNotRegistered) =>
1127711295
All(withNonPublic: withNonPublic, withPrimitive: false, withFields: false, withBase: withBase, ifUnresolved: ifUnresolved);
1127811296

11297+
#if SUPPORTS_REQUIRED_PROPERTIES
11298+
/// <summary>The rule to discover and inject the `required properties` introduced in C# 11 and .NET 7.
11299+
/// The properties are only injected if the constructor selected for the injection is NOT marked with `SetsRequiredMembers`,
11300+
/// because it says the constuctor is reponsible for the setting of required properties.</summary>
11301+
public static PropertiesAndFieldsSelector RequiredProperties() => _requiredProperties;
11302+
11303+
// We are keeping it as the field internally
11304+
// and still using the public accessor method to be consistent with the rest of the PropertiesAndFields API.
11305+
private static readonly PropertiesAndFieldsSelector _requiredProperties = req =>
11306+
{
11307+
var implType = req.ImplementationType;
11308+
if (implType == null)
11309+
return null;
11310+
11311+
var ctor = req.SelectedConstructor;
11312+
if (ctor != null && ctor.GetCustomAttribute<SetsRequiredMembersAttribute>() != null)
11313+
return null;
11314+
11315+
var props = implType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
11316+
if (props.Length == 0)
11317+
return null;
11318+
11319+
var requiredProps = props.Match(
11320+
p => p.GetCustomAttribute<RequiredMemberAttribute>() != null,
11321+
p => PropertyOrFieldServiceInfo.OfRequiredProperty(p));
11322+
return requiredProps;
11323+
};
11324+
#endif
11325+
1127911326
/// <summary>Should return service info for input member (property or field).</summary>
1128011327
public delegate PropertyOrFieldServiceInfo GetServiceInfo(MemberInfo member, Request request);
1128111328

@@ -14836,9 +14883,9 @@ public static IEnumerable<TMember> GetMembers<TMember>(
1483614883
{
1483714884
var typeInfo = type.GetTypeInfo();
1483814885
var members = getMembers(typeInfo);
14839-
if (!includeBase || typeInfo.BaseType == null || typeInfo.BaseType == typeof(object))
14840-
return members;
14841-
return members.Append(typeInfo.BaseType.GetMembers(getMembers, true));
14886+
return !includeBase || typeInfo.BaseType == null || typeInfo.BaseType == typeof(object)
14887+
? members
14888+
: members.Append(typeInfo.BaseType.GetMembers(getMembers, true));
1484214889
}
1484314890

1484414891
/// <summary>Returns all public instance constructors for the type</summary>

test/DryIoc.TestRunner/Program.cs

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ public static void Main()
1010
{
1111
RunAllTests();
1212

13+
// new RequiredPropertiesTests().Run();
14+
1315
// todo: @wip
1416
// new GHIssue565_Is_ignoring_ReuseScoped_setting_expected_behaviour_when_also_set_to_openResolutionScope().Run();
1517

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using NUnit.Framework;
2+
3+
namespace DryIoc.UnitTests
4+
{
5+
[TestFixture]
6+
public class RequiredPropertiesTests : ITest
7+
{
8+
#if !NET7_0_OR_GREATER
9+
public int Run() => 0;
10+
#else
11+
public int Run()
12+
{
13+
Can_inject_required_properties();
14+
return 1;
15+
}
16+
17+
[Test]
18+
public void Can_inject_required_properties()
19+
{
20+
var c = new Container(Rules.Default.With(propertiesAndFields: PropertiesAndFields.RequiredProperties()));
21+
22+
c.Register<S>();
23+
24+
c.Register<A>();
25+
c.Register<B>();
26+
c.Register<C>();
27+
c.Register<D>();
28+
29+
var x = c.Resolve<S>();
30+
31+
Assert.NotNull(x.A);
32+
Assert.NotNull(x.B);
33+
Assert.NotNull(x.C);
34+
Assert.NotNull(x.DD);
35+
}
36+
37+
public class A {}
38+
public class B {}
39+
public class C {}
40+
public class D {}
41+
42+
public class S : BS
43+
{
44+
public required A A { get; set; }
45+
public required B B { get; init; }
46+
public required C C { internal get; init; }
47+
public D DD => D;
48+
}
49+
50+
public class BS
51+
{
52+
public required D D { protected get; set; }
53+
}
54+
#endif
55+
}
56+
}

0 commit comments

Comments
 (0)