Получение конструктора типа интерфейса через отражение, есть ли лучший подход, чем циклическое прохождение типов? - PullRequest
1 голос
/ 17 июня 2010

Я написал общий тип: 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);
}

Из методов IDirectorySource<T>.ToList() я использую отражение, чтобы найти подходящий конструктор для параметра типа T.Однако, поскольку T задан тип interface , он вообще не может найти конструктор!

Конечно, у меня есть 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...
}

В методе 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...
}

Моя проблема в том, что T всегда будет interface, поэтому он никогда не найдет конструктор.

Есть ли лучший способ, чем перебирать все мои типы сборок для реализации моего интерфейса?

Меня не волнует переписывание части моего кода, я хочу сделать это правильново-первых, чтобы мне не нужно было возвращаться снова и снова и снова.

РЕДАКТИРОВАТЬ # 1

Следуя совету Сэма, я пока пойдус IName и Name соглашением.Тем не менее, это я или есть какой-то способ улучшить мой код?

Спасибо!=)

Ответы [ 3 ]

5 голосов
/ 17 июня 2010

У вас есть несколько возможностей здесь.

  • Сэм дал вам один ответ.
  • Используйте какой-нибудь контейнер, см. Внедрение зависимости .
  • Ограничьте тип T, чтобы он имел конструктор по умолчанию ( Ограничение конструктора ), и добавьтеa 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;   
  } 
}

Затем вы можете создать экземпляр DirectorySource следующим образом:

IDirectorySource<Group> groups = new DirectorySource<Group>(new GroupSearcher());
  • ... :)

РЕДАКТИРОВАТЬ: Вы также можете использовать лямбда-выражения.

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));

А в отношении обязанностей класса и тому подобное, есть ли у вас специальная обработка для каждого поддерживаемого типа внапример.Change(T) метод (или любой другой)?Если это так, то я все равно реорганизую и использую IDirectoryEntryManager<T> (или другое имя), которое будет знать, как обрабатывать соответствующий тип.DirectorySource мог бы затем использовать этот класс для манипулирования конкретными типами и не быть обремененным деталями, которые там не принадлежат.

3 голосов
/ 17 июня 2010

Вы можете перебрать все классы в сборке и найти тот, который реализует интерфейс.Вам нужно будет решить, что делать, когда вы найдете несколько реализаций.

Или, если у вас есть согласованная схема именования, вы можете сгенерировать имя класса из имени интерфейса.

2 голосов
/ 17 июня 2010

Вы можете создать специальный атрибут для указания типа реализации без необходимости полагаться на соглашение об именах.

[AttributeUsage(AttributeTargets.Interface)]
public class ImplementingTypeAttribute: Attribute
{
    public Type ImplementingType { get; set; }

    public ImplementingTypeAttribute(Type implementingType)
    {
        ImplementingType = implementingType;
    }
}

Но рефакторинг - отличная идея:)

...