[英]Create an instance of a List where the type derives from a base class and an interface
I have a class that will be storing objects that derive from a base class and implement an interface. 我有一个类,该类将存储从基类派生并实现接口的对象。 In this example, all the types derive from UIElement
and implement my interface IDropTarget
. 在此示例中,所有类型都派生自UIElement
并实现我的接口IDropTarget
。
So in my class I have I can use generic type inference in a fantastic way to require this without restricting everything to a specific base class 所以在我的课堂上,我可以用一种奇妙的方式使用泛型类型推断来要求它,而不必将所有内容都限制在特定的基类中
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();
}
}
Unfortunately, there seems to be no way for me to store this TTarget restricted list. 不幸的是,我似乎没有办法存储此TTarget受限列表。 I can't restrict it to a specific type because I have multiple classes that derive from UIElement already (Rectangle, Button, Grid, etc.), and there's no way for me to make all these object derive from a base type. 我不能将其限制为特定的类型,因为我已经有多个类从UIElement派生(矩形,按钮,网格等),而且我没有办法使所有这些对象都从基本类型派生。
My next solution is going to be storing a list of each type. 我的下一个解决方案是存储每种类型的列表。 I need to figure out if that overhead is worth it vs casting the objects each time I use them. 我需要弄清楚这些开销是否值得,而不是每次使用它们时都要投射对象。
You can not add two different types into a list this way. 您不能通过这种方式将两种不同的类型添加到列表中。 You are basically saying that you want they generic type to be either X
OR Y
. 你基本上是说,你想他们一般类型设置为X
或Y
。 Imagine what would happen when you did a foreach
, the compiler would not know if you wanted a UIElement
or a IDragDrop
. 想象一下,当您进行一次foreach
时会发生什么,编译器将不知道您是否需要UIElement
或IDragDrop
。
You just have to maintain type safety manually. 您只需要手动维护类型安全。 Use a private list (like you are doing, I assume) and then do the cast manually. 使用私有列表(我想就像您正在做的那样),然后手动进行投射。 It is up to you which type you want to use, or any. 取决于您要使用哪种类型或任何类型。
public class MyClass {
private _list = new List<object>(); // don't store any type info for now
public void AddDropTarget.... /* this is already correct */
public void RunUiStuff() {
// get all items that are of type UIElement. Items of wrong type will be ignored
foreach(var e in _list.OfType<UIElement>()) {
}
}
public void RunDropStuff() {
// cast works as well, but will throw if any object is not the correct type
foreach(var e in _list.Cast<IDropTarget>()) {
}
}
}
If you don't want cast you can create the common base class you need with a variant of the decorator pattern. 如果您不想强制转换,则可以使用装饰器模式的变体来创建所需的公共基类。
//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();}
}
with this, your list can be 这样,您的列表可以是
public List<IElementAndDropTarget> list = new List<IElementAndDropTarget>();
in AddDropTarget
you can populate it with the decorated classes as: 在AddDropTarget
,可以使用修饰的类将其填充为:
list.Add(UIElementDecorator.Make(target));
and that's it. 就是这样。 The rest of your code can stay the same. 您的其余代码可以保持不变。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.