听起来您需要的是接口类型,而不是委托。接口方法可以接受开放的泛型类型(这是您所追求的),即使委托不能。例如,可以定义如下内容:
interface ActOnConstrainedThing<CT1,CT2>
{
void Act<MainType>(MainType param) where MainType: CT1,CT2;
}
即使CT1
andCT2
的实现者不共享同样实现CT1
and的公共基类型CT2
,其实现Act
也可以将其传入的参数用作CT1
或CT2
不使用类型转换,甚至可以将其传递给期望带有CT1
和CT2
约束的泛型参数的例程。对于代表来说,这样的事情是不可能的。
请注意,使用接口而不是委托意味着不能使用正常的“事件”机制和语法。相反,作为事件发布者的对象必须维护实现所需接口的对象实例列表(例如 a List<ActOnConstrainedThing<IThis,IThat>>
),并枚举该列表中的实例(可能使用foreeach
)。例如:
List<IActOnConstrainedThing<IThis,IThat>> _ActOnThingSubscribers;
void ActOnThings<T>(T param) where T:IThis,IThat
{
foreach(_ActOnThingSubscribers 中的变量)
{
thing.Act<T>(参数);
}
}
编辑/附录
我使用这种模式的地方也有一些其他的东西似乎与问题不太相关,根据我的解释,这是询问如何拥有一个带有开放类型参数的委托(或等价物),以便对象调用委托等效项可以提供类型参数,而提供委托的对象不必事先知道它。大多数有用的情况都涉及通用约束,但由于这显然会引入混淆,所以这里有一个例子:
接口IShuffleFiveThings
{
void Shuffle<T>(ref T p1, ref T p2, ref T p3, ref T p4, ref T p5);
}
List<IShuffleFiveThings _ShuffleSubscribers;
void ApplyShuffles<T>(ref T p1, ref T p2, ref T p3, ref T p4, ref T p5)
{
foreach(_ShuffleSubscribers 中的 var shuffler)
{
thing.Shuffle(参考 p1,参考 p2,参考 p3,参考 p4,参考 p5);
}
}
该IShuffleFiveThings.Shuffle<T>
方法接受五个参数ref
并对其进行处理(很可能以某种方式排列它们;也许随机排列它们,或者可能随机排列一些而将其他的留在原处。如果有一个列表IShuffleFiveThings
,则该列表中的事物可以是有效地使用,无需装箱或反射,来操作任何类型的东西(包括类类型和值类型)。相比之下,如果要使用委托:
委托无效ActOn5RefParameters(参考p1,参考p2,参考p3,参考p4,参考p5);
那么因为任何特定的委托实例只能作用于其创建时提供的单个参数类型(除非它是一个仅通过反射调用的开放委托),因此需要为希望的每种类型的对象创建一个单独的委托列表shuffle(是的,我知道通常会使用整数索引数组来处理排列;我选择排列作为操作是因为它适用于所有对象类型,而不是因为这种特殊的排列方法很有用)。
请注意,由于类型T
inIShuffleFiveThings
没有任何约束,因此除了类型转换(可能会引入装箱)外,实现将无法对它做很多事情。向这些参数添加约束使它们更加有用。虽然可以在接口内硬编码此类约束,但这会限制接口对需要这些特定约束的应用程序的有用性。使约束本身具有通用性可以避免该限制。