将返回 T 的特定实现的方法分配给 Func<T> 变量
Assign a method returning a specific implementation of T to a Func<T> variable
我想分配一个返回泛型类型参数特定实现的方法给一个值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.
我从那里得到了原因(感谢@canton7)
The problem of course is that the typeof check is a runtime check, but
the compiler needs to assert at compile-time
如果我声明泛型类型并直接传递函数,它就可以工作。
//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
是值类型,则 that 每次调用时都会涉及装箱,而委托本身的转换则不会。
Jon 已经 post 完成了我要 post 的一半内容,因此我不会重复他的工作。但是,我确实想补充一点建议,因为我 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, Allocator allocator, 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;
}
您甚至可以减少重复代码,并且仍然支持涉及其他类型的场景,方法是保留通用版本但不允许该参数是可选的也不是空的:
public static NativeList<MyImplementationStruct> CreateSimulationListA(int amount, Allocator allocator, Func<MyImplementationStruct> simulator = null)
{
return CreateSimulationList(amount, allocator, simulator ?? SimulateA);
}
public static NativeList<MyImplementationStructB> CreateSimulationListB(int amount, Allocator allocator, Func<MyImplementationStructB> simulator = null)
{
return CreateSimulationList(amount, allocator, simulator ?? SimulateB);
}
public static NativeList<T> CreateSimulationList<T>(int amount, Allocator allocator, 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;
}
由于泛型类型推断在缺少参数的情况下无论如何都不起作用,您一定是使用显式类型参数调用该方法,因此自定义调用站点以通过其各自的名称调用非泛型方法应该不难吧。
以上反映了这样一个事实,即代码一开始似乎并不真正是通用的,因此试图将方法变成通用的是一个错误。最好将类型硬编码到非泛型方法中,并将泛型部分留给方法真正泛型的场景。
我想分配一个返回泛型类型参数特定实现的方法给一个值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 problem of course is that the typeof check is a runtime check, but the compiler needs to assert at compile-time
如果我声明泛型类型并直接传递函数,它就可以工作。
//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
是值类型,则 that 每次调用时都会涉及装箱,而委托本身的转换则不会。
Jon 已经 post 完成了我要 post 的一半内容,因此我不会重复他的工作。但是,我确实想补充一点建议,因为我 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, Allocator allocator, 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;
}
您甚至可以减少重复代码,并且仍然支持涉及其他类型的场景,方法是保留通用版本但不允许该参数是可选的也不是空的:
public static NativeList<MyImplementationStruct> CreateSimulationListA(int amount, Allocator allocator, Func<MyImplementationStruct> simulator = null)
{
return CreateSimulationList(amount, allocator, simulator ?? SimulateA);
}
public static NativeList<MyImplementationStructB> CreateSimulationListB(int amount, Allocator allocator, Func<MyImplementationStructB> simulator = null)
{
return CreateSimulationList(amount, allocator, simulator ?? SimulateB);
}
public static NativeList<T> CreateSimulationList<T>(int amount, Allocator allocator, 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;
}
由于泛型类型推断在缺少参数的情况下无论如何都不起作用,您一定是使用显式类型参数调用该方法,因此自定义调用站点以通过其各自的名称调用非泛型方法应该不难吧。
以上反映了这样一个事实,即代码一开始似乎并不真正是通用的,因此试图将方法变成通用的是一个错误。最好将类型硬编码到非泛型方法中,并将泛型部分留给方法真正泛型的场景。