Why is overloaded method differing in ref only CLS

2019-04-25 12:04发布

问题:

Common Language Specification is quite strict on method overloads.

Methods are allowed to be overloaded only based on the number and types of their parameters, and in the case of generic methods, the number of their generic parameters.

Why is this code CLS compliant (no CS3006 warning) according to csc?

using System;

[assembly: CLSCompliant (true)]

public class Test {
    public static void Expect<T>(T arg)
    {
    }

    public static void Expect<T>(ref T arg)
    {
    }

    public static void Main ()
    {
    }
}

回答1:

This is CLS-compliant because the types differ. The rules for overloading are requiring one (or more) of the criteria to be met, not all of them at the same time.

A ref T (or out T, which is using the same with same type different semantics) is declaring a "reference" to a T reference (for classes) or the instance (in case of value types).

For more details, look up the Type.MakeByRefType() method - it creates the type representing a reference to the original type, e.g. for a T this returns a T& (in C++ notation).



回答2:

To be clear, in general, overloaded methods differing only in ref or out, or in array rank, are not CLS-compliant, according to MSDN.

You can verify the compiler does indeed check for this specific case by writing a simple non-generic version:

using System;

[assembly: CLSCompliant (true)]

public class Test {
    public static void Expect(int arg)
    {
    }

    public static void Expect(ref int arg)
    {
    }

    public static void Main ()
    {
    }
}

However, you seem to have hit upon a compiler edge-case, since if you add in the generic method overloads, the compiler doesn't seem to complain.

I would say this is either a bug in the compiler (as in this similar question), or there is indeed a more relaxed specification for generics, since they are a latter addition to the specification.

I would err on the side of some kind of compiler limitation, given that this example also raises CS3006:

using System;

[assembly: CLSCompliant(true)]

public class Test<T>
{
    public static void Expect(T arg)
    {
    }

    public static void Expect(ref T arg)
    {
    }

    public static void Main()
    {
    }
}

Apparently adding the generics to the class, rather than the method, raises the compiler's attention...