通过反射获取接口类型的构造函数,是否有比循环类型更好的方法?

Wil*_*ler 1 c# generics reflection dependency-injection interface

我写了一个泛型类型:IDirectorySource<T> where T : IDirectoryEntry,我使用的是通过我的接口的对象来管理Active Directory中的条目,其中:IGroup,IOrganizationalUnit,IUser.

这样我就可以写下面的内容:

IDirectorySource<IGroup> groups = new DirectorySource<IGroup>(); // Where IGroup implements `IDirectoryEntry`, of course.`

foreach (IGroup g in groups.ToList()) {
    listView1.Items.Add(g.Name).SubItems.Add(g.Description);
}
Run Code Online (Sandbox Code Playgroud)

IDirectorySource<T>.ToList()方法中,我使用反射来找出类型参数的适当构造函数T.但是,由于T给定了一个接口类型,它根本找不到任何构造函数!

当然,我有一个internal class Group : IGroup实现IGroup接口的.无论我多么努力,我都无法弄清楚如何通过我的实现类从我的界面中获取构造函数.

[DirectorySchemaAttribute("group")]
public interface IGroup {
}

internal class Group : IGroup {
    internal Group(DirectoryEntry entry) {
        NativeEntry = entry;
        Domain = NativeEntry.Path;
    }
    // Implementing IGroup interface...
}
Run Code Online (Sandbox Code Playgroud)

ToList()我的IDirectorySource<T>接口实现方法中,我寻找T如下构造函数:

internal class DirectorySource<T> : IDirectorySource<T> {
    // Implementing properties...
    // Methods implementations...
    public IList<T> ToList() {
        Type t = typeof(T)

        // Let's assume we're always working with the IGroup interface as T here to keep it simple.
        // So, my `DirectorySchema` property is already set to "group".
        // My `DirectorySearcher` is already instantiated here, as I do it within the DirectorySource<T> constructor.
        Searcher.Filter = string.Format("(&(objectClass={0}))", DirectorySchema)

        ConstructorInfo ctor = null;
        ParameterInfo[] params = null;

        // This is where I get stuck for now... Please see the helper method.
        GetConstructor(out ctor, out params, new Type() { DirectoryEntry });

        SearchResultCollection results = null;

        try {
            results = Searcher.FindAll();
        } catch (DirectoryServicesCOMException ex) {
            // Handling exception here...
        }

        foreach (SearchResult entry in results)
            entities.Add(ctor.Invoke(new object() { entry.GetDirectoryEntry() }));

        return entities;            
    }
}

private void GetConstructor(out ConstructorInfo constructor, out ParameterInfo[] parameters, Type paramsTypes) {
    Type t = typeof(T);

    ConstructorInfo[] ctors = t.GetConstructors(BindingFlags.CreateInstance
                                                | BindingFlags.NonPublic
                                                | BindingFlags.Public
                                                | BindingFlags.InvokeMethod);

    bool found = true;

    foreach (ContructorInfo c in ctors) {
        parameters = c.GetParameters();

        if (parameters.GetLength(0) == paramsTypes.GetLength(0)) {
            for (int index = 0; index < parameters.GetLength(0); ++index) {
                if (!(parameters[index].GetType() is paramsTypes[index].GetType()))
                    found = false;
            }
            if (found) {
                constructor = c;
                return;
            }
        }
    }

    // Processing constructor not found message here...
}
Run Code Online (Sandbox Code Playgroud)

我的问题是T永远是一个interface,所以它永远不会找到一个构造函数.

有没有比循环我的所有程序集类型更好的方法来实现我的界面?

我不关心重写我的代码,我想在第一个地方做到这一点,这样我就不需要一次又一次地回来.

编辑#1

根据Sam的建议,我现在将遵循INameName惯例.但是,是我还是有办法改进我的代码?

谢谢!=)

Pat*_*tko 5

你有几种可能性.

  • 山姆给了你一个答案.
  • 使用某种容器,参见Depencency injection.
  • 将类型T约束为具有默认构造函数(构造函数约束)并将SetEntry(DirectoryEntry)添加到IDirectoryEntry接口.
  • 重构您的代码,以便您的目录源不会因创建新实例而负担沉重.你可能有不同的搜索者,每个都返回正确的类型.

class DirectorySource<T>: IDirectorySource<T>  {   
  public DirectorySource(ISearcher<T> searcher) {
    Searcher = searcher;   
  }   
  public IList<T> ToList()    {
    string filter = "...";
    return Searcher.FindAll(filter);   
  } 
}     
class GroupSearcher: ISearcher<Group> {
  public IList<Group> FindAll(string filter)    {
    entries = ...
    var entities = new List<Group>();
    foreach (var entry in entries) 
      entities.Add(new Group(entry.GetDirectoryEntry());
    return entities;   
  } 
}
Run Code Online (Sandbox Code Playgroud)

然后,您将像这样实例化DirectorySource:

IDirectorySource<Group> groups = new DirectorySource<Group>(new GroupSearcher());
Run Code Online (Sandbox Code Playgroud)
  • ...... :)

编辑: 你也可以使用lambda表达式.

class DirectorySource<T>: IDirectorySource<T> {
  // Or you could put the parameter in constructor if this is not the only place
  // where you create new instances of T
  public IList<T> ToList(Func<DirectoryEntry, T> create) {
    ...
    foreach (var entry in entries)
      entities.Add(create(entry.GetDirectoryEntry()));
    return entities;
  }
}

IList<Group> groups = new DirectorySource<Group>().ToList(entry => new Group(entry));
Run Code Online (Sandbox Code Playgroud)

关于班级责任等,您是否对每种支持类型进行了特殊处理,例如:Change(T)方法(或任何其他)?如果是这样,那么我仍然会重构和使用IDirectoryEntryManager<T>(或其他一些名称)知道如何处理适当的类型.DirectorySource然后可以使用该类来操纵具体类型,而不是负担不属于那里的细节.