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

Ark*_*kun 8 c# generics delegates function-pointers strong-typing

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

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

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

泛型类是具有一些未指定的" 类型参数 "的类.您提供了类型参数,然后您可以使用该类 - 调用构造函数或调用静态方法.

通用功能在非泛型类是具有一些未指定"的功能类型参数 "和一些未指定" 值参数 ".您提供了type-argumentsvalue-arguments来接收结果.

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

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

static class SomeClass {
    //generic function
    public static T GetValue<T>() {
        return default(T);
    }
}

//creating delegate to generic function or method group
Func{TFree}<TFree> valueFactory = SomeClass.GetValue;

//creating delegate to anonymous generic function
Func{TFree}<int, List<TFree>> listFactory = {TFree}(int capacity) => new List<TFree>(capacity);
Run Code Online (Sandbox Code Playgroud)

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

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

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

public static class Factory { //Everything compiles fine here
    public delegate ICollection<T> FactoryDelegate<T>(IEnumerable<T> values);

    public static ICollection<T> CreateList<T>(IEnumerable<T> values) {
        return new List<T>(values);
    }

    public static ICollection<T> CreateSet<T>(IEnumerable<T> values) {
        return new HashSet<T>(values);
    }
}

public class Worker { //non-generic class
    Func{TFree}<FactoryDelegate<TFree>> _factory; //TFree is a "free" generic type paramenter

    public Worker(Func{TFree}<FactoryDelegate<TFree>> factory) {
        _factory = factory;
    }

    public ICollection<T> DoWork<T>(IEnumerable<T> 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<string> result1 = listWorker.DoWork(values1);
        ICollection<int> result2 = listWorker.DoWork(values2); //.Count == 4
        ICollection<int> result3 = setWorker.DoWork(values2); //.Count == 2
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

Mik*_*ray 8

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

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

public interface IWorker
{
    ICollection<T> DoWork<T>(IEnumerable<T> values);
}

public class ListCreationWorker : IWorker
{
    public ICollection<T> DoWork<T>(IEnumerable<T> values)
    {
        return Factory.CreateList<T>(values);
    }
}

public class SetCreationWorker : IWorker
{
    public ICollection<T> DoWork<T>(IEnumerable<T> values)
    {
        return Factory.CreateSet<T>(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<string> result1 = listWorker.DoWork(values1);
        ICollection<int> result2 = listWorker.DoWork(values2); //.Count == 4
        ICollection<int> result3 = setWorker.DoWork(values2); //.Count == 2
    }
}

public static class Factory
{
    public static ICollection<T> CreateSet<T>(IEnumerable<T> values)
    {
        return new HashSet<T>(values);
    }

    public static ICollection<T> CreateList<T>(IEnumerable<T> values)
    {
        return new List<T>(values);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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