微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

将返回 T 的特定实现的方法分配给 Func<T> 变量

如何解决将返回 T 的特定实现的方法分配给 Func<T> 变量

我想将返回泛型类型参数的特定实现的方法分配给值 Func<T>。 T 被约束为非托管。 Func 将用于填充列表。

本计划是使用检查 (typeof(T) == typeof(int) 找到类型 T,但代码无法编译。

public static List<T> CreateSimulationList<T>(int amount,Func<T> simulator = null)
    where T : unmanaged
{
    if (simulator == null)
    {
        //this sends an error: "has the wrong return type"
        if (typeof(T) == typeof(int)) { simulator = FillWithZeroInt; }

        //this sends an error: "has the wrong return type"        
        if (typeof(T) == typeof(float)) { simulator = FillWithOneFloat; }
    }

    List<T> list = new List<T>(amount);
    for (int i = 0; i < amount; i++)
    {
        list.Add(simulator());
    }
    return list;
}

private static int   FillWithZeroInt()  => 0;
private static float FillWithOneFloat() => 1f;

有趣的是,Rider 并未在 IDE 内报告该问题。但是 Unity 编译器可以:

'MyImplementationStruct SimulateA' has the wrong return type.
'MyImplementationStructB SimulateB' has the wrong return type.

我从the comments那里得到了原因(感谢@canton7)

问题当然是typeof检查是运行时检查,但是 编译器需要在编译时断言

如果我声明泛型类型并直接传递函数,它就可以工作。

//this code works
var generatedList = CreateSimulationList<int>(10,FillWithZeroInt);

我想在编译时推断 T 的类型并分配一个方法,或者找到任何其他方法来避免在方法参数中设置 Func<T>,并且没有装箱。

感谢您阅读本文。

解决方法

首先,这有点设计味道 - 在泛型方法中使用特定类型的匹配通常并不理想。我承认偶尔这是必需的。

您可以通过强制转换来使其工作...您需要强制转换为具体的 Func 类型以开始(或使用另一种形式的委托初始化),然后转换为 object 基本上阻止编译器认为它知道什么是有效的,然后转换为 Func<T>。这是一个完整的示例:

using System;

class Program
{
    static void Main()
    {
        var func1 = CreateFunc<int>();
        var func2 = CreateFunc<string>();
        Console.WriteLine(func1);
        Console.WriteLine(func2);
    }
    
    static Func<T> CreateFunc<T>()
    {
        if (typeof(T) == typeof(int))
        {
            return (Func<T>) (object) (Func<int>) Int32Func;
        }
        if (typeof(T) == typeof(string))
        {
            return (Func<T>) (object) (Func<string>) StringFunc;
        }
        return null;
    }
    
    static int Int32Func() => 0;
    static string StringFunc() => "";
}

另一种替代方法是使用 lambda 表达式并将方法调用的结果转换为 T。同样,您需要在转换为 object 之前转换为 T

if (typeof(T) == typeof(int))
{
    return () => (T) (object) Int32Func();
}
if (typeof(T) == typeof(string))
{
    return () => (T) (object) StringFunc();
}

请注意,如果 T 是值类型, 每次调用都会涉及装箱,而委托本身的转换则不会。

,

Jon 已经发布了我要发布的一半内容,因此我不会重复他的工作。但是,我确实想补充一点建议,因为我 100% 同意他关于“设计气味”和“不理想”的说明(恕我直言),我建议您应该将您的实现分解为特定于类型的方法。

换句话说,与其让方法通用,不如让两个方法拥有你想要处理的签名:

public static NativeList<MyImplementationStruct> CreateSimulationListA(int amount,Allocator allocator,Func<MyImplementationStruct> simulator = null)
{
    simulator = simulator ?? SimulateA;

    NativeList<T> list = new NativeList<T>(amount,allocator);
    for (int i = 0; i < amount; i++)
    {
        list.Add(simulator());
    }
    return list;
}

public static NativeList<MyImplementationStructB> CreateSimulationListB(int amount,Func<MyImplementationStructB> simulator = null)
{
    simulator = simulator ?? SimulateB;

    NativeList<T> list = new NativeList<T>(amount,allocator);
    for (int i = 0; i < amount; i++)
    {
        list.Add(simulator());
    }
    return list;
}

您甚至可以通过保留泛型版本但不允许该参数为可选或 null 来减少重复代码,并仍然支持涉及其他类型的场景:

public static NativeList<MyImplementationStruct> CreateSimulationListA(int amount,Func<MyImplementationStruct> simulator = null)
{
    return CreateSimulationList(amount,allocator,simulator ?? SimulateA);
}

public static NativeList<MyImplementationStructB> CreateSimulationListB(int amount,Func<MyImplementationStructB> simulator = null)
{
    return CreateSimulationList(amount,simulator ?? SimulateB);
}

public static NativeList<T> CreateSimulationList<T>(int amount,Func<T> simulator)
    where T : unmanaged,IMyInterface
{
    NativeList<T> list = new NativeList<T>(amount,allocator);
    for (int i = 0; i < amount; i++)
    {
        list.Add(simulator());
    }
    return list;
}

由于泛型类型推断在缺少参数的情况下无论如何都不起作用,因此您必须使用显式类型参数调用该方法,因此自定义调用站点以通过其各自的名称调用非泛型方法应该'不辛苦。

以上反映了这样一个事实,即代码一开始似乎并不真正是通用的,因此尝试将方法变为通用是错误的。最好将类型硬编码为非泛型方法,而将泛型部分留给方法可以真正泛型的场景。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。