2013-12-13 1 views
2

У меня есть класс, который будет хранить объекты, которые происходят из базового класса и реализуют интерфейс. В этом примере все типы производятся от UIElement и реализуют мой интерфейс IDropTarget.Создайте экземпляр списка, в котором тип получен из базового класса и интерфейса

Так что в моем классе у меня есть, я могу использовать общий вывод типа в фантастический способ требует этого, не ограничивая все для конкретного базового класса

public void AddDropTarget<TTarget>(TTarget target) 
    where TTarget : UIElement, IDropTarget 
{ 
    target.DoSomethingAsUIElement(); 
    target.DoSomethingAsIDropTraget(); 

    // Now I want to save it for another method 
    list.Add(target); 
} 

public void DoStuff() 
{ 
    foreach(var item in list) 
    { 
     item.MoreUIElementAndDropStuff(); 
    } 
} 

К сожалению, кажется, нет никакого способа для меня, чтобы сохранить этот список ограничений TTarget. Я не могу ограничить его определенным типом, потому что у меня есть несколько классов, которые уже получены из UIElement (Rectangle, Button, Grid и т. Д.), И мне не удастся сделать все эти объекты производными от базового типа.

Следующее решение будет хранить список каждого типа. Мне нужно выяснить, стоит ли это накладные расходы, и бросать объекты каждый раз, когда я их использую.

+0

Почему вы не делаете 'list = new List ();'? – Tony

+1

В вашем случае вы можете добавить 'UIElement AsUIElement {get; } 'к вашему интерфейсу. Затем вы можете получить список «IDropTarget» и сделать что-то вроде 'foreach (элемент var в списке) {item.AsUIElement.Focus(); } ' –

+0

Тони: Я не могу использовать Список , потому что мне также нужен доступ к IDropTarget. –

ответ

0

Если вы не хотите бросить, вы можете создать общий базовый класс, который вам нужен, с вариантом декоратора.

//our new base type 
public interface IElementAndDropTarget : IDropTarget 
{ 
    void DoSomethingAsUIElement(); 
    void MoreUIElementAndDropStuff(); 
} 

// our decorator. We need to store UIElement and IDropTarget 
// as different fields. The static "Make" is giving you 
// the compile-time guarantee that they both refer 
// to the same class 
public class UIElementDecorator : IElementAndDropTarget 
{ 
    private readonly UIElement t; 
    private readonly IDropTarget dt; 

    private UIElementDecorator(UIElement t, IDropTarget dt) 
    { 
     this.t=t; 
     this.dt=dt; 
    } 

    public static UIElementDecorator Make<T>(T t) where T: UIElement, IDropTarget 
    { 
     return new UIElementDecorator(t,t); 
    } 

    public void DoSomethingAsUIElement() {t.DoSomethingAsUIElement();} 
    public void MoreUIElementAndDropStuff(){t.MoreUIElementAndDropStuff();} 
    public void DoSomethingAsIDropTarget() {dt.DoSomethingAsIDropTarget();} 
} 

с этим, ваш список может быть

public List<IElementAndDropTarget> list = new List<IElementAndDropTarget>(); 

в AddDropTarget вы можете заполнить его с украшенными классов как:

list.Add(UIElementDecorator.Make(target)); 

и это все. Остальная часть вашего кода может оставаться неизменной.

+0

Я знаю, что это работает, я просто не знаю, почему вы пройдете эту боль, особенно если вам нужна больше площади поверхности из UIElement/DropTarget, чем один или два метода. Он становится громоздким быстро. Просто ознакомьтесь с базовыми свойствами 'UIElement' и' DropTarget' и избавьтесь от методов как интерфейса, так и декоратора. Тогда вы также можете избавиться от интерфейса ... теперь нам это не нужно. Таким образом, все, что у вас есть, - это простой класс, содержащий объект в двух предустановленных публичных свойствах и статический заводский метод для создания новых экземпляров. –

+0

@AndrewBacker достаточно справедливо. Если для делегирования имеется 10 или более методов, то дополнительная инкапсуляция может не стоить всех ручных поэтапных делегаций. –

0

Вы не можете добавить два разных типа в список таким образом. Вы в основном говорите, что хотите, чтобы общий тип был либоX OR Y. Представьте себе, что произойдет, когда вы сделали foreach, компилятор не знал бы, хотите ли вы UIElement или IDragDrop.

Вам просто нужно поддерживать безопасность типа вручную. Используйте частный список (например, вы делаете, я полагаю), а затем сделайте бросок вручную. Это зависит от вас, какой тип вы хотите использовать, или любой.

+0

Вывод generics работает над этим методом, поэтому во время компиляции он может проверить, что параметр оба происходит из UIElement, и реализовать IDropTarget. Чтобы быть ясным, мне не нужен тип, который может быть либо X, либо Y, я хочу, чтобы оба они точно так же, как и определение TTarget. Компилятор знает, что параметр должен быть обоим, и поэтому я могу использовать методы из обоих типов в одной и той же переменной. Я понял, что не было никакого способа получить то же самое в Списке, но это стоило спросить. И так как generics обеспечивают безопасность типа, мне не нужно беспокоиться о '.Cast <>' throwing anyway. –

+0

Конечно :) Они два разных типа, не могут объединить их без нового базового класса. –

+0

Я думаю, вы не поняли вопрос. В моем примере выше 'AddDropTraget ', TTarget фактически является обоими типами. Это ОБА ОБОРОТ И ИДРОПРЕЙТ. Ограничения общего типа гарантируют это. TTarget относится к любому типу, который наследуется от UIElement и реализует IDropTarget. Поэтому я мог бы создать класс под названием MyDropButton: Button, IDropTarget и еще один класс под названием MyDropTextBlock: TextBlock, IDropTarget. Оба эти класса соответствуют ограничению для TTarget, но мне не удалось создать список , который может содержать оба таких типа, как AddDropTarget. –