如何在 C# 中泛化委托

本文关键字:泛化 | 更新日期: 2023-09-27 18:35:49

我正在尝试创建一个类似于以下内容的委托序列生成器(这对于单元测试中的存根很有用):

    public class StubSequenceBuilder<T>
    {
        private readonly Queue<Func<T>> _sequenceQueue = new Queue<Func<T>>();
        public StubSequenceBuilder<T> Next(Func<T> func)
        {
            _sequenceQueue.Enqueue(func);
            return this;
        }
        public Func<T> Build()
        {
            return () =>
            {
                var func = _sequenceQueue.Dequeue();
                return func();
            };
        } 
    }

序列生成器创建一个Func<T>,该每次调用时都返回不同的值。

但是,这仅适用于没有参数并返回值的委托。有没有办法泛化这个类以支持任何委托?

我知道类型约束不支持委托;C# 中还有其他东西可以用来实现这一点吗?

如何在 C# 中泛化委托

C# 中没有任何直接方法可以做到这一点,但您可以使用类似于以下文本模板 (*.tt) 的内容:

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ output extension=".cs" #>
using System;
using System.Linq;
using System.Collections.Generic;
<#
var maxArguments = 5;
for(int i=0; i < maxArguments; ++i) {
    var argTypes = string.Join(", ",new string[i+1].Select((x,j)=>"T"+j).ToArray());
#>
public class StubSequenceBuilder<<#= argTypes #>>
{
    private readonly Queue<Func<<#= argTypes #>>> _sequenceQueue = new Queue<Func<<#= argTypes #>>>();
    public StubSequenceBuilder<<#= argTypes #>> Next(Func<<#= argTypes #>> func)
    {
        _sequenceQueue.Enqueue(func);
        return this;
    }

    //...
}
<# } #>

输出 (*.cs):

using System;
using System.Linq;
using System.Collections.Generic;
public class StubSequenceBuilder<T0>
{
    private readonly Queue<Func<T0>> _sequenceQueue = new Queue<Func<T0>>();
    public StubSequenceBuilder<T0> Next(Func<T0> func)
    {
        _sequenceQueue.Enqueue(func);
        return this;
    }

    //...
}
public class StubSequenceBuilder<T0, T1>
{
    private readonly Queue<Func<T0, T1>> _sequenceQueue = new Queue<Func<T0, T1>>();
    public StubSequenceBuilder<T0, T1> Next(Func<T0, T1> func)
    {
        _sequenceQueue.Enqueue(func);
        return this;
    }

    //...
}
//...