6

I 'm trying to build a DI container and I 've stumbled on to the following problem: I have a method that retrieves a list of registered instances for a given type and I want to use that to inject IEnumerable<T> properties in a given object. An example of what I am trying to achieve would be the following:

class A { public IList<IExample> Objects { get; set; } }
class B: IExample {}
class C: IExample {}
Container.Register<IExample>(new B());
Container.Register<IExample>(new C());
var obj = new A();
Container.Inject(A);
Debug.Assert(A.Objects != null && A.Objects.Count == 2);

My Retrieve method returns an IList<object>, mainly because I have no type information at that moment, so I am attempting to convert that list into a List<T> at injection time. Here is a succint form of the methods doing the work:

public virtual IList<object> Retrieve(Type type)
{
    var instances = Registry[type];
    foreach(var instance in instances) 
        Inject(type, instance); // omitted
    return instances;
}

public virtual void Inject<T>(T instance)
{
    var properties = typeof (T).GetProperties();
    foreach (var propertyInfo in properties)
    {
      var propertyType = propertyInfo.PropertyType;
      if (!IsIEnumerable(propertyType)) continue;
      var genericType = propertyType.GetGenericArguments()[0];
      propertyInfo.SetValue(instance, 
          GetListType(genericType, Retrieve(genericType)), null);
    }
}

protected virtual object GetListType(Type type, IEnumerable<object> items)
{
    return items.Select(item => Convert.ChangeType(item, type)).ToList();
}

The code returns the error: System.InvalidCastException : Object must implement IConvertible. Sadly, I don't know how to proceed from here. Perhaps I am doing this all wrong. I 've thought of using generics or injecting multiple properties by hand, but I'd really like to not have to do that.

Thanks in advance for any help or ideas.


You could create a generic list like this:

public virtual IList Retrieve(Type type)
{
  // ...
  listType = typeof(List<>).MakeGenericType(new Type[] { type });
  IList list = (IList)Activator.CreateInstance(listType);
  // ...
  return list
}

this list can be casted to IList<T>, because it is one.

You could consider to use IEnumerable and Cast<T>, but then you don't have an instance of a list. I don'^t know how important it is to have one.

4

1 回答 1

9

您可以像这样创建一个通用列表:

public virtual IList Retrieve(Type type)
{
  // ...
  listType = typeof(List<>).MakeGenericType(new Type[] { type });
  IList list = (IList)Activator.CreateInstance(listType);
  // ...
  return list
}

此列表可以转换为IList<T>,因为它是一个。

您可以考虑使用IEnumerableand Cast<T>,但是您没有列表的实例。我不知道拥有一个有多重要。

于 2012-08-15T08:13:46.577 回答