c#类型系统支持表示匹配约束的类型



问题:

public class Test
{
public void A<T>(T arg)
{
// We have proof it's safe to call B
if (arg is IEquatable<T>)
{
// To call B, something like this needed.
// var arg1 = (T:IEquatable<T>) arg;
// B(arg1)
B(arg); // Error CS0314: The type 'T' cannot be used as type parameter 'T' in the generic type or method 'Test.B<T>(T)'.
// There is no boxing conversion or type parameter conversion from 'T' to 'System.IEquatable<T>'.
}
else
{
C(arg);
}
}
public void B<T>(T arg)
where T: IEquatable<T>
{
// We don't want to loos the original type T that has been passed to A<T>
Console.WriteLine(typeof(T).Name);
}
public void C<T>(T arg)
{
}
}

如果我们有证据证明它是安全的,那么我们是否有任何c#语言结构允许调用更多具有更多类型限制的泛型方法(更高类型多态性,高级模式匹配等)?

我们是否有任何选择来做一个奇怪的不安全的hack来进行不允许的泛型方法调用?看起来我们可以在IL级别表达调用(Fody + https://github.com/ltrzesniewski/InlineIL.Fody)或以某种方式可以使用函数指针。(反射当然可以提供帮助,但它的成本很高,并且可能导致CoreRT本地编译的问题)。

如果你的参数类型是IEquatable<T>,而不是使用类型约束呢?

您对T类型一无所知,因此不值得使用它来代替IEquatable<T>类型作为参数类型。

class Test
{
public void A<T>(T arg)
{
if (arg is IEquatable<T> equatable)
{
B1(equatable);
}
else
{
C(arg);
}
B1(2);
B2(2);
}
public void B1<T>(IEquatable<T> arg)
{
// This should write the original type T that has been passed to A<T>
// because if a type implements IEquatable<>
// then the type parameter should be the same as the implementing type.
// Just what your type constraint expects, anyway.
Console.WriteLine(typeof(T).FullName);
// But you can also get the real T type by using .GetType() method.
Console.WriteLine(arg.GetType());
// Should output true.
Console.WriteLine(typeof(T) == arg.GetType());
}
// You can keep this method as an overload,
// if you really need it for some reason.
public void B2<T>(T arg) where T : IEquatable<T>
{
B1(arg);
}
public void C<T>(T arg)
{
}
}

要调用B,需要这样的东西。

var arg1 = (T:IEquatable<T>) arg

是的,这是问题的核心。不,这样的语言特性是不存在的。

要按原样调用B1,您需要构造您的解决方案,以便调用者可以提供与其签名匹配的类型。唯一的选择是反射。

所以你的选择是放松B1的约束。也许通过将实现移动到一个更容易从A调用的新的私有方法。或者重载A<T>,这样调用者就可以证明T:IEquatable<T>.

这个问题的解决方案在这里跟踪:https://github.com/dotnet/csharplang/discussions/905