0

我不确定这两个签名是否有任何真正的区别:

public static class MyCustomExtensions
{
    public static bool IsFoo(this IComparable<T> value, T other)
        where T : IComparable<T>
    {
        // ...
    }

    public static bool IsFoo(this T value, T other)
        where T : IComparable<T>
    {
        // ...
    }
}

我认为这些基本上会以几乎相同的方式运行,但我不太确定......我在这里忽略了什么?

4

4 回答 4

5

就在这里。

第一个签名将匹配任何可以与 进行比较的类型T,而不仅仅是T值。所以任何实现的类型IComparable<int>都可以被第一个签名使用,而不仅仅是int.

例子:

void Main()
{
    10.IsFoo(20).Dump();
    new Dummy().IsFoo(20).Dump();

    IComparable<int> x = 10;
    x.IsFoo(20).Dump();

    IComparable<int> y = new Dummy();
    y.IsFoo(20).Dump();
}

public class Dummy : IComparable<int>
{
    public int CompareTo(int other)
    {
        return 0;
    }
}

public static class Extensions
{
    public static bool IsFoo<T>(this IComparable<T> value, T other)
        where T : IComparable<T>
    {
        Debug.WriteLine("1");
        return false;
    }

    public static bool IsFoo<T>(this T value, T other)
        where T : IComparable<T>
    {
        Debug.WriteLine("2");
        return false;
    }
}

将输出:

2
False
1
False
1
False
1
False

我用LINQPad对此进行了测试。

于 2013-05-08T21:55:06.040 回答
0

如果我们稍微重写它来使用IList而不是IComparable,那不是同一个问题吗?

在那种情况下,很明显这IsFoo1与 完全不同IsFoo2
因为IsFoo1接受本质上的第一个论点IList<IList<T>>
IsFoo2接受公正的第一个论点IList<T>

public static class MyCustomExtensions
{
    public static bool IsFoo1(IList<T> value, T other)
        where T : IList<T>
    {
        // ...
    }

    public static bool IsFoo2(T value, T other)
        where T : IList<T>
    {
        // ...
    }
}

所以不,它们根本不一样。

于 2013-05-08T21:56:35.420 回答
0

区别非常明显。请注意,您必须T在方法(泛型方法)或包含类(泛型类,不能使用扩展方法)中定义。下面我称这两种方法1和2:

public static bool IsFoo1<T>(this IComparable<T> value, T other)
    where T : IComparable<T>
{
    return true;
}

public static bool IsFoo2<T>(this T value, T other)
    where T : IComparable<T>
{
    return true;
}

根据T是值类型还是引用类型,存在差异。您可以通过使用约束where T : struct, IComparable<T>where T : class, IComparable<T>.

通常使用任何类型TX可能会声明 一些疯狂的类型IComparable<Y>,其中YX.

使用值类型:

withIFoo1第一个参数value会被装箱,而valueinIFoo2不会被装箱。值类型是密封的,逆变不适用于值类型,所以这是本例中最重要的区别。

使用参考类型:

使用 reference type T,装箱不是问题。但请注意,它的类型参数IComparable<>是逆变的(“ in”)。如果某些非密封类实现,这很重要IComparable<>。我使用了这两个类:

class C : IComparable<C>
{
    public int CompareTo(C other)
    {
        return 0;
    }
}
class D : C
{
}

使用它们,可以进行以下调用,其中一些是因为继承和/或逆变:

        // IsFoo1

        new C().IsFoo1<C>(new C());
        new C().IsFoo1<C>(new D());
        new D().IsFoo1<C>(new C());
        new D().IsFoo1<C>(new D());

        new C().IsFoo1<D>(new D());
        new D().IsFoo1<D>(new D());

        // IsFoo2

        new C().IsFoo2<C>(new C());
        new C().IsFoo2<C>(new D());
        new D().IsFoo2<C>(new C());
        new D().IsFoo2<C>(new D());

        //new C().IsFoo2<D>(new D()); // ILLEGAL
        new D().IsFoo2<D>(new D());

当然,在许多情况下,<C>可以省略通用参数,因为它会被推断出来,但为了清楚起见,我将其包含在此处。

于 2013-05-09T10:49:32.990 回答
0

它们并不相同。在第一个中,您传递IComparable<T>给第一个而不是第二个,因此您的实际类型将是<IComparable<IComparable<T>>and IComparable<T>

根据 Lee 的反馈进行编辑:下面的这些看起来相同,但是虽然两者都需要 value 和 other 来实现 IComparable,但第二个还要求它们可以分配给 T。

public static bool IsFoo<T>(IComparable<T> value, IComparable<T> other)
{
    // ...
}

public static bool IsFoo<T>(T value, T other)
    where T : IComparable<T>
{
    // ...
}
于 2013-05-08T22:04:29.427 回答