I have a generic class with the following constructors 1. Map(int resolution)
, 2. Map(int resolution, T defaultValue)
, 3. Map(int width, int height)
and 4. Map(int width, int height, T defaultValue)
, what happens if one of the inheriting classes requires map<int>, constructors 2 and 3 each have 2 parameters and in the map<int> case they would both be integers.
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);
}
}
CodePudding user response:
You can use named arguments to disambiguate the methods.
//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);
The same will work with the base
keyword.
class MyClass : Map<int>
{
public MyClass(int width, int defaultValue) : base( width: width, defaultValue: defaultValue)
{
}
}
CodePudding user response:
This is technically possible in the CLI. The ECMA-335 specification specifically points this out:
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 calledm
, all with the same parameter type; and two fields calledf
with the same type. They are all distinguished through theMemberRef
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]
In the case of C#, this is not considered illegal and will use standard method overload resolution:
- Otherwise, given any two members of the set,
M
andN
, apply the following tie-breaking rules, in order:
..snip..
bv. Before type arguments have been substituted, ifM
is less generic (Section Genericity) thanN
, eliminateN
from the set.
So the ones which are non-generic are taken in favour of the ones which are generic.
Note that the overload resolution only takes into account information available at compile-time (dynamic
excepted). So the constructor
public Map(int resolution, T defaultValue) : this(resolution, resolution, defaultValue) {}
will always call
public Map(int width, int height, T defaultValue) {
and never call this one even for a Map<int>
public Map(int width, int height, int defaultValue) {