两个C#扩展泛型方法之间的模糊调用,其中T:class和其他T:struct

Lee*_*son 45 .net c# generics extension-methods

考虑两种扩展方法:

public static T MyExtension<T>(this T o) where T:class
public static T MyExtension<T>(this T o) where T:struct
Run Code Online (Sandbox Code Playgroud)

一节课:

class MyClass() { ... }
Run Code Online (Sandbox Code Playgroud)

现在在上面的类的实例上调用extension方法:

var o = new MyClass(...);
o.MyExtension(); //compiler error here..
o.MyExtension<MyClass>(); //tried this as well - still compiler error..
Run Code Online (Sandbox Code Playgroud)

编译器说当我在类上调用它时调用该方法是一个模糊的调用.我原以为它可以确定调用哪个扩展方法,因为MyClass是一个类,而不是一个结构体?

Jon*_*eet 35

编辑:我现在更详细地在博客上写这篇文章.


我的原始(我现在认为不正确)认为:在重载决策和类型推断阶段不考虑通用约束 - 它们仅用于验证重载决策的结果.

编辑:好的,经过很多努力,我想我就在那里.基本上我的第一个想法几乎是正确的.

通用类型约束仅用于在非常有限的情况下从候选集中移除方法...特别是,仅当参数本身的类型是通用的时; 不仅仅是一个类型参数,而是一个使用泛型类型参数的泛型类型.此时,它是对已验证的泛型类型的类型参数的约束,而不是您正在调用的泛型方法的类型参数的约束.

例如:

// Constraint won't be considered when building the candidate set
void Foo<T>(T value) where T : struct

// The constraint *we express* won't be considered when building the candidate
// set, but then constraint on Nullable<T> will
void Foo<T>(Nullable<T> value) where T : struct
Run Code Online (Sandbox Code Playgroud)

所以如果你试图调用Foo<object>(null)上面的方法将不会成为候选集的一部分,因为Nullable<object> value无法满足约束条件Nullable<T>.如果有任何其他适用的方法,则呼叫仍然可以成功.

现在在上面的例子中,约束完全相同......但它们不一定是.例如,考虑:

class Factory<TItem> where TItem : new()

void Foo<T>(Factory<T> factory) where T : struct
Run Code Online (Sandbox Code Playgroud)

如果你试图调用Foo<object>(null),该方法将仍然是候选集的一部分-因为当TItemobject,表示对约束Factory<TItem>仍然成立,这是建立在候选集时发生了什么检查.如果这是最好的方法,那么它将在稍后的7.6.5.1 结束时失败验证:

如果最佳方法是泛型方法,则根据泛型方法上声明的约束(第4.4.4节)检查类型参数(提供或推断).如果任何类型参数不满足类型参数上的相应约束,则会发生绑定时错误.

Eric的博客文章包含更多细节.


Cou*_*y D 10

Eric Lippert 在这里解释得比我更好.

我自己也遇到过这种情况.我的解决方案是

public void DoSomthing<T> (T theThing){
    if (typeof (T).IsValueType)
        DoSomthingWithStruct (theThing);
    else
        DoSomthingWithClass (theThing);  
}

// edit - seems I just lived with boxing

public void DoSomthingWithStruct (object theThing)
public void DoSomthingWithClass(object theThing)
Run Code Online (Sandbox Code Playgroud)


Sal*_*iti 5

我发现使用默认参数值在 .NET 4.5 中使用这种“有趣”的奇怪方法:) 也许对于教育\推测目的比实际使用更有用,但我想展示它:

/// <summary>Special magic class that can be used to differentiate generic extension methods.</summary>
public class MagicValueType<TBase>
    where TBase : struct
{
}

/// <summary>Special magic class that can be used to differentiate generic extension methods.</summary>
public class MagicRefType<TBase>
    where TBase : class
{
}

struct MyClass1
{
}

class MyClass2
{
}

// Extensions
public static class Extensions
{
    // Rainbows and pink unicorns happens here.
    public static T Test<T>(this T t, MagicRefType<T> x = null)
        where T : class
    {
        Console.Write("1:" + t.ToString() + " ");
        return t;
    }

    // More magic, other pink unicorns and rainbows.
    public static T Test<T>(this T t, MagicValueType<T> x = null)
        where T : struct
    {
        Console.Write("2:" + t.ToString() + " ");
        return t;
    }
}

class Program
{
    static void Main(string[] args)
    {

        MyClass1 t1 = new MyClass1();
        MyClass2 t2 = new MyClass2();

        MyClass1 t1result = t1.Test();
        Console.WriteLine(t1result.ToString());

        MyClass2 t2result = t2.Test();
        Console.WriteLine(t2result.ToString());

        Console.ReadLine();
    }
}
Run Code Online (Sandbox Code Playgroud)