Как мне перечислить все элементы, которые реализуют общий интерфейс? - PullRequest
4 голосов
/ 10 июня 2009

У меня есть два интерфейса, универсальный и неуниверсальный, которые имеют иерархию наследования:

public interface IGenericRelation<TParent, TChild> : IRelation

public interface IRelation

Общий реализован несколькими серверными элементами управления, которые загружаются динамически, и я хочу перечислить коллекцию элементов управления, которые реализуют этот интерфейс. Я могу сделать следующее

    foreach (IRelation relationControl in this.uiPlhControls.Controls.OfType<IRelation)
    { ... }

Но то, что я действительно хотел бы сделать, это ...

    foreach (IGenericRelation<,> relationControl in this.uiPlhControls.Controls.OfType<IGenericRelation<,>)
    { ... }

и затем я смогу использовать relationControl с типами, которые он предоставил, тогда у меня был бы доступ к строго типизированным свойствам, доступным в IGenericRelation. К сожалению, это невозможно, так как кажется, что я не могу опустить параметры типа.

Кто-нибудь знает способ перечислить элементы управления, которые реализуют универсальный интерфейс, чтобы мне не пришлось писать несколько циклов вместо одного? Возможно, с помощью отражения?

Ответы [ 2 ]

3 голосов
/ 10 июня 2009

Это невозможно, поскольку IGenericRelation<T,F> является совершенно отличным типом от IGenericRelation<G,I>. Если вам нужен доступ к определенным свойствам, которые являются общими для всех IGenericRelation, то вам нужно либо реализовать их на уровне IRelation, либо ввести третий интерфейс между IRelation и IGenericRelation<,>, который реализует эти , Причина этого заключается в том, что у компилятора нет средств, позволяющих определить, какие типы ожидать от него реализации.

Самый простой способ сделать это - реализовать два ваших свойства как object на более высоком уровне (либо IRelation или промежуточный интерфейс) и строго типизировать на уровне IGenericRelation<,>.

1 голос
/ 10 июня 2009

К каким строго типизированным свойствам вы пытаетесь получить доступ? Если они строго типизированы, потому что они являются входными типами универсального, вы не сможете получить к ним доступ, не предоставив типы в цикле foreach. Если они строго типизированы, но не связаны с предоставленными типами, можете ли вы переместить их в класс IRelation?

Это будет иметь больше смысла с примером кода - скажем, что ваши классы примерно такие:

public IRelation
{
   public string RelationshipType { get; set; }
}

public IGenericRelation<TParent, TChild> : IRelation
{
    public TParent Parent { get; set; }
    public TChild Child { get; set; }
}

Если ваш список содержал одну IGenericRelation<Foo, Bar> и одну IGenericRelation<Fizz, Buzz>, вы не можете перечислить и получить оба обратно, не зная, какой конкретный тип вы ищете:

//Theoretical, non-compiling example....
foreach (IGenericRelation<,> relationControl in this.uiPlhControls.Controls.OfType<IGenericRelation<,>>)
{ 
    //This wouldn't work for type IGenericRelation<Fizz, Buzz>
    relationControl.Parent.FooProperty = "Wibble";

    //You would be able to access this, but there is no advantage over using IRelation
    relationControl.RelationshipType = "Wibble";
}

(Обратите внимание, что мне также пришлось изменить типlationControl в foreach из кода вашего примера, чтобы возможное использование имело некоторый смысл.)


По сути, полезно думать о дженериках .NET так же, как о шаблонных классах C ++ (я знаю, что реализация отличается, но эффект в этом отношении тот же). Представьте, что во время компиляции весь ваш код проверяется на предмет использования класса IGenericRelation, и создаются конкретные неуниверсальные классы, выполняя поиск по ключевым словам TParent и TChild и заменяя их запрошенным типом. Поскольку два созданных класса являются такими же отдельными, как и любые два других класса .NET, нет смысла запрашивать «все классы, которые начинались как этот шаблон», лучшее, что вы можете сделать, - это найти общий базовый класс или интерфейс - в этом случае IRelation.

...