Reputation: 6556
I have types that looks like the following:
public class Test1
{
[SubTests]
List<SubTest1> SubTests { get; set; } = [];
public void Run()
{
foreach(var subtest in SubTests)
{
// ...
}
}
public class SubTest1 : ISubTest { }
}
public class Test2
{
[SubTests]
List<SubTest2> SubTests { get; set; } = [];
public void Run()
{
foreach(var subtest in SubTests)
{
// ...
}
}
public class SubTest2 : ISubTest { }
}
public interface ISubTest { }
I want to use reflection to add the elements of Test.SubTests
to a BindingList<ISubTest>
, but I don't know the type of the ISubTest
at compile time.
I tried getting the list like this:
foreach (var property in type.GetProperties())
{
if (property.GetCustomAttribute<SubTestsAttribute>() != null)
{
var subTests = (IList<ISubStep>)property.GetValue(valueSource);
return new BindingList<ISubstep>(subTests)
}
}
But that throws an InvalidCastException
because the type of the generic parameter to IList<>
is different.
Is there any way to do what I'm trying to do?
Upvotes: 0
Views: 44
Reputation: 4380
I think you need to manually add elements with foreach. IList<T>
is not covariant (no out
).
foreach (var property in type.GetProperties()) {
if (property.GetCustomAttribute<SubTestsAttribute>() != null) {
var subTests = (IEnumerable<ISubStep>)property.GetValue(valueSource);
var bindingList= new BindingList<ISubstep>();
foreach (var element in subTests) {
bindingList.Add(element);
}
return bindingList;
}
}
EDIT: FWIW, I didn't know this was a duplicate when answering - but thanks for the two downvotes. That definitely should teach me a lesson in what the "consensus" is.
Upvotes: -1
Reputation: 828
This require 2 steps:
First, you need to create the BindingList. For this, you can reuse the same generic argument type:
List<int> myList = [];
// Create the binding list by using the same List<> argument
var argumentType = myList.GetType().GenericTypeArguments[0];
var bindingListType = typeof(BindingList<>).MakeGenericType(argumentType);
var bindingList = Activator.CreateInstance(bindingListType);
// Now you have a BindingList<int> instance
Second step is to populate the list. I don't think the BindingList is contravariant, so I don't think you can cast it to a BidingList<>. But you can use reflexion:
var addMethodInfo = bindingListType.GetMethod("Add");
addMethodInfo.Invoke(bindingList, itemToAdd);
You probably can do both at once by using a different constructor:
var argumentType = myList.GetType().GenericTypeArguments[0];
var bindingListType = typeof(BindingList<>).MakeGenericType(argumentType);
var bindingList = Activator.CreateInstance(bindingListType, subTests);
// The reflexion will look for a constructor that accept one argument of type `subTests.GetType()`
Upvotes: 0