在C#中使用免费generics类型参数模拟委托

这是一个关于语言设计,模式和语义的难题。 请不要因为你没有看到实际价值而进行投票。

首先,让我们考虑一下函数及其参数。 然后我们将看看函数与其参数/参数和generics类/函数及其类型参数/类型参数之间的类比。

函数是代码块,带有一些未指定的值,称为“ 参数 ”。 您提供参数并接收结果。

generics类是具有一些未指定的“ 类型参数 ”的类。 您提供了类型参数 ,然后您可以使用该类 – 调用构造函数或调用静态方法。

非generics类中的generics函数是具有一些未指定的“ 类型参数 ”和一些未指定的“ 值参数的函数 。 您提供了type-argumentsvalue-arguments来接收结果。

代表是指向特定function的指针。 创建委托时,不要指定函数参数 ,而是稍后提供它们。

问题是.Net与具有未指定generics类型参数的generics函数没有等效的Delegates。 您以后不能为type-parameters提供类型值 。 我们可以想象代理不仅有自由值参数 ,还有自由类型参数

static class SomeClass { //generic function public static T GetValue() { return default(T); } } //creating delegate to generic function or method group Func{TFree} valueFactory = SomeClass.GetValue; //creating delegate to anonymous generic function Func{TFree}<int, List> listFactory = {TFree}(int capacity) => new List(capacity); 

下面是我想用C#编写的程序的[伪]代码。 我想知道如何在正确的C#程序中实现类似的行为。

我们如何在C#中使用免费的generics类型参数模拟委托?

我们如何通过非generics代码将引用/链接传递给具有未知通用参数的generics函数[s]?

 public static class Factory { //Everything compiles fine here public delegate ICollection FactoryDelegate(IEnumerable values); public static ICollection CreateList(IEnumerable values) { return new List(values); } public static ICollection CreateSet(IEnumerable values) { return new HashSet(values); } } public class Worker { //non-generic class Func{TFree}<FactoryDelegate> _factory; //TFree is a "free" generic type paramenter public Worker(Func{TFree}<FactoryDelegate> factory) { _factory = factory; } public ICollection DoWork(IEnumerable values) { //generic method return _factory{T}(values); //supplying T as the argument for type parameter TFree } } public static class Program { public static void Main() { string[] values1 = new string[] { "a", "b", "c" }; int[] values2 = new int[] { 1, 2, 2, 2 }; Worker listWorker = new Worker(Factory.CreateList); //passing reference to generic function Worker setWorker = new Worker(Factory.CreateSet); //passing reference to generic function ICollection result1 = listWorker.DoWork(values1); ICollection result2 = listWorker.DoWork(values2); //.Count == 4 ICollection result3 = setWorker.DoWork(values2); //.Count == 2 } } 

看看我们如何将对generics函数(Factory.CreateList和Factory.CreateSet)的引用传递给Worker类构造函数而不指定类型参数 ? 稍后在使用具体类型的数组调用通用DoWork函数时提供类型参数 。 DoWork使用类型参数来选择正确的函数,将值参数传递给它并返回接收的值。

最终解决方案: 使用C#中的免费generics类型参数模拟委托

我认为你在语言中模仿这种方式的方法是不使用委托而是使用接口。 非generics接口可以包含generics方法,因此您可以使用开放类型参数获取委托的大部分行为。

以下是您重新设计为有效C#程序的示例(请注意,它仍然需要您定义的Factory类):

 public interface IWorker { ICollection DoWork(IEnumerable values); } public class ListCreationWorker : IWorker { public ICollection DoWork(IEnumerable values) { return Factory.CreateList(values); } } public class SetCreationWorker : IWorker { public ICollection DoWork(IEnumerable values) { return Factory.CreateSet(values); } } public static class Program { public static void Main(string[] args) { string[] values1 = new string[] { "a", "b", "c" }; int[] values2 = new int[] { 1, 2, 2, 2 }; IWorker listWorker = new ListCreationWorker(); IWorker setWorker = new SetCreationWorker(); ICollection result1 = listWorker.DoWork(values1); ICollection result2 = listWorker.DoWork(values2); //.Count == 4 ICollection result3 = setWorker.DoWork(values2); //.Count == 2 } } public static class Factory { public static ICollection CreateSet(IEnumerable values) { return new HashSet(values); } public static ICollection CreateList(IEnumerable values) { return new List(values); } } 

您仍然可以获得将要调用的方法与执行所述方法的决策分开的重要function。

但是,您不能做的一件事是以通用方式将任何状态存储在IWorker实现中。 我不确定它是如何有用的,因为每次都可以使用不同的类型参数调用DoWork方法。

在.Net的类型系统下,这实际上没有意义。

你所描述的是一个类型构造函数 – 一个“函数”,它接受一种或多种类型并返回一个具体的( 参数化的封闭的 )类型。

问题是类型构造函数本身不是类型。 您不能拥有开放类型的对象或变量; 类型构造函数只能用于生成具体类型。

换句话说,没有办法在.Net的类型系统中表示对开放函数的引用。


你能做的最好就是用reflection; MethodInfo可以描述开放的generics方法。
通过编写一个带有伪通用参数的表达式树的generics方法,您可以获得对打开的MethodInfo的编译时类型安全引用:

 public MethodInfo GetMethod(Expression method) { //Find the MethodInfo and remove all TPlaceholder parameters } GetMethod(() => SomeMethod(...)); 

如果要引用具有该参数约束的开放generics方法,则TPlaceholder参数是必需的; 您可以选择符合约束的占位符类型。

解决方案是接口。 正如@ mike-z所写,接口支持generics方法。 因此,我们可以使用generics方法创建非generics接口IFactory,该方法在某些类中包含对generics方法的引用。 要使用这样的接口绑定[Factory]类的generics方法,我们通常需要创建实现IFactory接口的小类。 它们就像lambdas使用的闭包一样。

我没有看到这和我要求的generics方法委托之间存在很大的语义差异。 解决方案非常类似于编译器为lambdas做的[只调用其他方法](使用调用它的方法创建闭包)。

我们失去了什么? 主要是语法糖。

  • 匿名函数/ lambdas。 我们无法创建通用lambdas。 能够创建匿名类(如在Java中)可以解决问题。 但这并不是一个问题,因为lambda只是.Net中的语法糖。

  • 能够从方法组隐式创建委托/链接(C#term)。 如果它是通用的,我们不能以任何方式使用方法组。 这也不会影响语义。

  • 定义通用委托的能力受到阻碍。 我们不能用方法V Create(U arg)一个通用的IFactory接口。 这也不是问题。

这是解决方案的代码。 问题中的Factory类没有变化。

 public interface IFactory { ICollection Create(IEnumerable values); } public class Worker { //not generic IFactory _factory; public Worker(IFactory factory) { _factory = factory; } public ICollection DoWork(IEnumerable values) { //generic method return _factory.Create(values); } } public static class Program { class ListFactory : IFactory { public ICollection Create(IEnumerable values) { return Factory.CreateList(values); } } class SetFactory : IFactory { public ICollection Create(IEnumerable values) { return Factory.CreateSet(values); } } public static void Main() { string[] values1 = new string[] { "a", "b", "c" }; int[] values2 = new int[] { 1, 2, 2, 2 }; Worker listWorker = new Worker(new ListFactory()); Worker setWorker = new Worker(new SetFactory()); ICollection result1 = listWorker.DoWork(values1); ICollection result2 = listWorker.DoWork(values2); //.Count == 4 ICollection result3 = setWorker.DoWork(values2); //.Count == 2 } }