好的,这很棘手。说我有一个清单:
List<Foo> MyList;
Foo
是一个常见的抽象类,用于系统中的所有组件。其中一些组件类是通用的:
public class Bar<T> : Foo
{
public void MethodCommonToAllBar()
{
...
}
}
有些类可以扩展通用类:
public class Baz : Bar<int>
public class Bat : Bar<string>
我希望从MyList
中提取一组Bar<anything>
类型的所有对象,以便我可以在每个事件上调用MethodCommonToAllBar()
。如果它不是通用的,我可以使用如下行:
foreach (Baz baz in MyList.Where(x => x is Baz))
{
baz.MethodCommonToAllBar();
}
foreach (Bat bat in MyList.Where(x => x is Bat))
{
bat.MethodCommonToAllBar();
}
甚至
foreach (Bar<int> bar in MyList.Where(x => x is Bar<int>))
{
bar.MethodCommonToAllBar();
}
foreach (Bar<string> bar in MyList.Where(x => x is Bar<string>))
{
bar.MethodCommonToAllBar();
}
但是除非我分别处理每个潜在的子类型,否则似乎无法获得任何类型Bar
的所有对象。这有解决方法吗?假设我无法以任何方式改变类结构。 (我可以,但它是一个很大的项目,这样的改变会影响很多人,所以我不太可能让它获得批准。理想情况下,我会添加另一个非 - 层次结构中Foo
和Bar<T>
之间的通用类,用于存储该方法和任何其他此类常见事物,并且只是搜索它。但是在游戏中稍微重新构建现在的重构。)我还可以看到一个涉及Reflection的解决方法,但这似乎有些过度,并且可能会导致性能损失。有没有其他办法可以干净利落地处理这个问题?
答案 0 :(得分:3)
您必须意识到Bar<X>
和Bar<Y>
这两种类型不兼容!它们只是源自共同祖先的两种不同类型。它们都是相同的开放泛型类型的具体实现这一事实没有任何区别。
你想如何处理它们?假设您从列表中检索到了一个未知泛型类型Bar<?>
的对象,并且您有一个方法
void ProcessBar<T>() { ... }
您从哪里知道T
的正确价值?请注意T
在编译时解析,而不是在运行时解析!这是仿制药的用途:通过保持类型安全性来提供一定的灵活性。类型安全只能在编译时实现。
解决此问题的一种方法实际上是在继承层次结构中引入非泛型中间类型。如果您无法更改继承层次结构,请让所有Bar<T>
实现非通用接口IBar
public interface IBar
{
void MethodCommonToAllBar();
}
只需使用
过滤您的列表foreach (IBar bar in MyList.OfType<IBar>())
{
bar.MethodCommonToAllBar(); // Method defined in IBar.
}
请注意OfType<>
同时执行此操作,过滤并转换为所需类型。
另一种方法是根本不使用泛型,并使用object
类中的Bar
类型,以获得最大的动态灵活性。
类型安全性和动态灵活性是对手。
答案 1 :(得分:2)
如果Bar<T>.MethodCommonToAllBar
方法不依赖于类型T
,则可以为所有条形定义一个通用接口:
public interface IBar
{
void MethodCommonToAllBar();
}
public class Bar<T> : Foo, IBar
{
public void MethodCommonToAllBar()
{
...
}
}
然后您就可以将任何Bar<T>
投射到此界面:
foreach (IBar bar in MyList.OfType<IBar>())
{
bar.MethodCommonToAllBar();
}
答案 2 :(得分:2)
嗯,有一些概念,如逆变,协方差。
在C#中,协方差和逆变使隐式参考成为可能 转换数组类型,委托类型和泛型类型 arguments.Covariance保留赋值兼容性和 逆转扭转它。 (Eric Lippert)
http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx
它可能或者它可能没有帮助你。基本上,如果您的类实现IFoo<out T>
,则可以在不知道实际泛型类型的情况下将该类视为IFoo<object>
。
答案 3 :(得分:0)
这个怎么样?
MyList.OfType<Bar<string>>().ToList()
.ForEach(item => item.MethodCommonToAllBar());