我知道我正在做的事情可以用不同的方式完成,但我对事情的运作方式很好奇。以下是一个不编译的简化代码,但它应该显示我的目标。
private void Execute()
{
GeneralizedFunction("1", "2", i => Transform(i));
}
void GeneralizedFunction(string aStringA, string aStringB, Func<string, T> aAction)
{
A result1 = aAction(aStringA);
B result2 = aAction(aStringB);
// Do something with A and B here
}
T Transform<T>(string aString)
{
return default(T);
}
Transform
是从字符串到某个对象的通用转换(想想反序列化)。
GeneralizedFunction
使用两个转换特化:一个用于类型A,一个用于类型B.我知道我可以通过许多其他方式执行此操作(例如,通过引入对象类型的参数),但是我我正在寻找解释是否有可能或不可能使用泛型/ lambdas。如果Transform在作为参数传递给GeneralizedFunction之前是专用的,那么这是不可能的。那么问题是为什么这种可能性受到限制。
答案 0 :(得分:7)
这个答案并没有解释为什么的原因,只是如何来解决这个限制。
您可以传递具有此类函数的对象,而不是传递实际函数:
interface IGenericFunc
{
TResult Call<TArg,TResult>(TArg arg);
}
// ... in some class:
void Test(IGenericFunc genericFunc)
{
// for example's sake only:
int x = genericFunc.Call<String, int>("string");
object y = genericFunc.Call<double, object>(2.3);
}
对于您的特定用例,可以简化为:
interface IDeserializerFunc
{
T Call<T>(string arg);
}
// ... in some class:
void Test(IDeserializerFunc deserializer)
{
int x = deserializer.Call<int>("3");
double y = deserializer.Call<double>("3.2");
}
答案 1 :(得分:4)
单独使用仿制药是不可能要做的。编译器需要生成Transform
函数的两个类型版本:一个用于返回类型A
,另一个用于类型B
。编译器无法知道在编译时生成它;只有通过运行代码才能知道A和B是必需的。
解决问题的一种方法是传递两个版本:
private void Execute()
{
GeneralizedFunction("1", "2", i => Transform<A>(i), i => Transform<B>(i));
}
void GeneralizedFunction(string aStringA, string aStringB, Func<string, A> aAction, Func<string, B> bAction)
{
A result1 = aAction(aStringA);
B result2 = bAction(aStringB);
}
编译器确切知道在这种情况下需要生成什么。
答案 2 :(得分:1)
尝试以下签名:
void GeneralizedFunction<T>(string aStringA, string aStringB, Func<string, T> aAction)
(注意,GeneralizedFunction必须是通用的;编译器会在调用方法时自动猜测类型参数。)
答案 3 :(得分:1)
似乎答案是“不”。
直接拨打Transform
时,您必须指定一个类型参数:
int i = Transform<int>("");
所以假设,如果你可以像你想要的那样传递一个不完整构造的泛型函数,你还需要指定类型参数:
void GeneralizedFunction(string aStringA, string aStringB, Func<string, T> aAction)
{
A result1 = aAction<A>(aStringA);
B result2 = aAction<B>(aStringB);
// Do something with A and B here
}
所以在我看来,如果C#有这样的语法,你可以假设这样做。
但是用例是什么?除了将字符串转换为任意类型的默认值之外,我认为没有太多用处。你如何定义一个函数,它可以使用相同的一系列语句在两种不同类型中提供有意义的结果?
修改强>
分析为什么不可能:
在代码中使用lambda表达式时,它会被编译为委托或表达式树;在这种情况下,它是一个代表。您不能拥有“开放”泛型类型的实例;换句话说,要从泛型类型创建对象,必须指定所有类型参数。换句话说,如果不为其所有类型参数提供参数,就无法拥有委托实例。
C#编译器的一个有用功能是隐式方法组转换,其中方法的名称(“方法组”)可以隐式转换为表示该方法的一个重载的委托类型。类似地,编译器隐式地将lambda表达式转换为委托类型。在这两种情况下,编译器都会发出代码来创建委托类型的实例(在这种情况下,将其传递给函数)。但是该委托类型的实例仍然需要为每个类型参数都有一个类型参数。
要将泛型函数作为泛型函数传递,似乎编译器需要能够传递方法组或 lambda表达式< / em>到方法没有转换,因此aAction
参数会以某种方式具有“方法组”或“lambda表达式”的类型。然后,可以在呼叫站点A result1 = aAction<A>(aStringA);
和B result2 = aAction<B>(aStringB);
上隐式转换为委托类型。当然,在这一点上,我们很好地融入了反事实和假设的世界。
我在午餐时提出的解决方案就是这样,假设函数Deserialize<T>
采用包含序列化数据的字符串并返回T
类型的对象:
void GeneralizedFunction<T>(string aStringA, string aStringB, Func<T, string> stringGetter)
{
A result1 = Deserialize<A>(stringGetter(aStringA));
B result2 = Deserialize<B>(stringGetter(aStringB));
}
void Example(string serializedA, string serializedB, string pathToA, string pathToB, FileInfo a, FileInfo b)
{
GeneralizedFunction(serializedA, serializedB, s => s);
GeneralizedFunction(pathToA, pathToB, File.ReadAllText);
GeneralizedFunction(a, b, fi => File.ReadAllText(fi.FullName));
}
答案 4 :(得分:0)
void GeneralizedFunction<T>(string aStringA, string aStringB, Func<string, T> aAction)
{
A result1 = aAction(aStringA);
B result2 = aAction(aStringB);
}
T Transform<T>(string aString)
{
return default(T);
}