在 C# 中使用免费的泛型类型参数模拟委托 [英] Emulating delegates with free generic type parameters in C#

查看:18
本文介绍了在 C# 中使用免费的泛型类型参数模拟委托的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个关于语言设计、模式和语义的难题.请不要因为看不到实用价值就投反对票.

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

First, let's think about functions and their parameters. Then we'll look at the analogies between functions with their parameters/arguments and generic classes/functions with their type-parameters/type-arguments.

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

Functions are blocks of code with some unspecified values called "parameters". You supply the arguments and receive the result.

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

Generic classes are classes with some unspecified "type-parameters". You supply the type-arguments and then you can work with the class - call the constructor or invoke static methods.

泛型函数是具有一些未指定的type-parameters"和一些未指定的value-parameters"的函数.您提供 type-argumentsvalue-arguments 来接收结果.

Generic functions in non-generic classes are functions with some unspecified "type-parameters" and some unspecified "value-parameters". You supply the type-arguments and value-arguments to receive result.

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

Delegates are pointers to specific functions. When you create delegate you don't specify the function arguments, but supply them later.

问题在于 .Net 对于具有未指定的泛型类型参数的泛型函数没有等效的委托.以后不能为 type-parameters 提供 type-values.我们可以想象委托不仅有免费的值参数,还有免费的类型参数.

The problem is that .Net doesn't have equivalent of Delegates for generic functions with unspecified generic type-parameters. You cannot supply type-values for the type-parameters later. We can imagine delegates that have not only free value parameters, but also free 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);

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

Below is the [pseudo]code for a program that I want to write in C#. I want to know how one can achieve the similar behavior in a correct C# program.

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

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

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
    }
}

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

See how we pass the references to generic functions (Factory.CreateList and Factory.CreateSet) to the Worker class constructor without specifying the type arguments? Type arguments are supplied later when the generic DoWork function is called with concrete-typed arrays. DoWork uses the type-arguments to select the correct function, passes value-arguments to it and returns the received value.

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

推荐答案

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

I think the way you emulate this in the language is by not using delegates but interfaces. A non-generic interface can contain a generic method, so you can get most of the behavior of delegates with open type arguments.

这是您的示例重新加工成一个有效的 C# 程序(请注意,它仍然需要您定义的 Factory 类):

Here is your example re-worked into a valid C# program (Note that it still requires the Factory class you defined):

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);
    }
}

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

You still get the important feature of separating the decision of which method to call from the execution of said method.

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

One thing that you cannot do, however, is store any state in the the IWorker implementations in a generic fashion. I'm not sure how that could be useful because the DoWork method could be called with different type arguments every time.

这篇关于在 C# 中使用免费的泛型类型参数模拟委托的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆