泛型如何处理构造函数

How do Generics handle constructors

我有一个泛型 class,它具有以下构造函数 1. Map(int resolution)2. Map(int resolution, T defaultValue)3. Map(int width, int height)4. Map(int width, int height, T defaultValue),如果其中一个继承 classes 需要 map,构造函数 2 和 3 各有 2 个参数,在 map 的情况下它们都是整数。

public abstract class Map<T> {
    private T[,] m_Map;
    private T m_DefaultValue;

    public T this[int x, int y] {
        get {
            return InRange(x,y)?m_Map[x,y]:DefaultValue;
        }
        set {
            if(InRange(x,y)) { // Included to handle confusion between retrieving a non-existent position, and setting one.
                m_Map[x,y]  value;
            }
        }
    }

    public int Width{get { return m_Map.GetLength(0); } }
    public int Height{get { return m_Map.GetLength(1); } }
    public virtual T DefaultValue { get { return m_DefaultValue; } set{ m_DefaultValue = value; } }

    public Map(int resolution) : this(resolution, resolution, default) {}
    public Map(int resolution, T defaultValue) : this(resolution, resolution, defaultValue) {}
    public Map(int width, int height) : this(width, height, default) {}
    public Map(int width, int height, T defaultValue) {
        m_Map = new T[width, height];
        m_DefaultValue = defaultValue;
    }

    public bool InRange(int x, int y) {
        return x>=0&&x<m_Map.GetLength(0)&&y>=0&&y<m_Map.GetLength(1);
    }
}

您可以使用 named arguments 来消除方法的歧义。

//Call Map(int,int)
var x = new Map<int>(width: 1, height: 2);

//Call Map(int, T)
var x = new Map<int>(width: 1, defaultValue: 3);

同样适用于 base 关键字。

class MyClass : Map<int>
{
    public MyClass(int width, int defaultValue) : base( width: width, defaultValue: defaultValue)
    {
    }
}

这在 CLI 中在技术上是可行的。 ECMA-335 specification 特别指出:

II.9.8 Signatures and binding

It is possible for distinct members to have identical types when instantiated, but which can be distinguished by MemberRef.
[Example:

.class public C`2<S,T> {
.field string f
.field !0 f
.method instance void m(!0 x) {...}
.method instance void m(!1 x) {...}
.method instance void m(string x) {...}
}

The closed type C``2<string,string> is valid: it has three methods called m, all with the same parameter type; and two fields called f with the same type. They are all distinguished through the MemberRef encoding described above:

string C`2<string, string>::f
!0 C<string, string>::f
void C`2<string, string>::m(!0)
void C`2<string, string>::m(!1)
void C`2<string, string>::m(string)

The way in which a source language might resolve this kind of overloading is left to each individual language. For example, many might disallow such overloads. end example]

在 C# 的情况下,这不被认为是非法的,将使用 standard method overload resolution:

  1. Otherwise, given any two members of the set, M and N, apply the following tie-breaking rules, in order:
    ..snip..
    bv. Before type arguments have been substituted, if M is less generic (Section Genericity) than N, eliminate N from the set.

所以非通用的优先于通用的。

请注意,重载决策仅考虑编译时可用的信息(dynamic 除外)。所以构造函数

public Map(int resolution, T defaultValue) : this(resolution, resolution, defaultValue) {}

会一直打电话

public Map(int width, int height, T defaultValue) {

即使 Map<int>

也永远不要调用这个
public Map(int width, int height, int defaultValue) {