-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Description
Related: #83599, #92137, #91324
The runtime binder will use the runtime type when deciding what members to bind, however the source generator will only use the referenced type - which may be a base type.
We could improve this by including cast tests to derived types when handling a base type's member.
EG:
public static void BindCore(IConfiguration configuration, ref Base instance)
{
if (instance is Derived1 derived1)
BindCore(configuration, ref derived1);
else if (instance is Derived2 derived2)
BindCore(configuration, ref derived2);
else if (instance is Derived3 derived3)
BindCore(configuration, ref derived3);
else
{
// abstract binding logic/
}
}We might be able to use some common type multiplexing method to do this, like a bind for object that checks for casting to various types, but it would need to be careful of the order in which to examine types.
This approach would create code overlap in handling of members on base types -- we have this overlap today. If we wanted to try and remove that we could make base bind methods responsible for binding base members - at least for cases where the initialization strategy is parameterless construct.
In addition to how we code-gen this we'd need to think about how we discover derived types. Do we always consider derived types that might be in the compilation? Do we add some API for developers to express which derived types we should look for?
Reproduction Steps
Note: requires fix for #92137 project: configAbstract.zip
using Microsoft.Extensions.Configuration;
var c = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string,string?>()
{
["Value"] = "Hello",
["Member:Value"] = "Hello world",
["Member:Value2"] = "Hello world2"
})
.Build();
MyBase x = new Derived()
{
Member = new Derived()
};
c.Bind(x);
Console.WriteLine(x.Value);
Console.WriteLine(x.Member?.Value);
Console.WriteLine(((Derived?)x.Member)?.Value2);
Derived d = new();
c.GetSection("Member").Bind(d);
Console.WriteLine(d.Value);
Console.WriteLine(d.Value2);
public abstract class MyBase
{
public MyBase() {}
public virtual string? Value {get; set;}
public MyBase? Member { get; set;}
}
public class Derived : MyBase
{
public Derived() : base()
{ }
public string? Value2 { get; set; }
}Expected behavior
Ideally, the derived type will bind the same way, regardless of bind call:
Hello
Hello world
Hello world2
Hello world
Hello world2
Actual behavior
Only the members in the reference type are bound.
Hello
Hello world
Hello world
Hello world2
Regression?
No, but it's a gap between runtime binder and source-gen binder.
Known Workarounds
No response
Configuration
No response
Other information
No response