Расширение на примере, чтобы выделить проблему:
public class BidirectionalMap<T1,T2>
{
public void Remove(T1 item) {}
public void Remove(T2 item) {}
public static void Test()
{
//This line compiles
var possiblyBad = new BidirectionalMap<string, string>();
//This causes the compiler to fail with an ambiguous invocation
possiblyBad.Remove("Something");
}
}
Таким образом, ответ таков: даже если вы не можете указать ограничение T1! = T2, это не имеет значения, потому что компилятор потерпит неудачу, как только вы попытаетесь сделать что-то, что нарушит неявное ограничение. Он все еще ловит сбой во время компиляции, так что вы можете использовать эти перегрузки безнаказанно. Это немного странно, так как вы можете создать экземпляр карты (и даже написать IL-код, который соответствующим образом управляет картой), но компилятор C # не позволит вам нанести ущерб, произвольно решая неоднозначные перегрузки.
Одно замечание: перегрузка такого рода может вызвать странное поведение, если вы не будете осторожны. Если у вас есть BidirectionalMap<Animal, Cat>
и Cat: Animal, подумайте, что произойдет с этим кодом:
Animal animal = new Cat();
map.Remove(animal);
Это вызовет перегрузку, которая забирает Animal, поэтому он попытается удалить ключ, даже если вы намеревались удалить значение Cat. Это несколько искусственный случай, но этого достаточно, чтобы оправдать осторожность, когда в результате перегрузки методов возникают очень разные варианты поведения. В таких случаях, вероятно, легче читать и поддерживать, если вы просто дадите методам разные имена, отражающие их различное поведение (скажем, RemoveKey и RemoveValue.)