Универсальный класс с двумя неравными (уникальными) типами - PullRequest
7 голосов
/ 09 ноября 2011

Можно ли реализовать класс, ограниченный двумя уникальными общими параметрами?

Если это не так, это потому, что он не реализован или потому что это невозможно из-за структуры языка (наследования)?

Я хотел бы что-то в форме:

class BidirectionalMap<T1,T2> where T1 != T2
{
  ...
}

Я реализую Двунаправленный словарь . Это в основном вопрос любопытства, а не необходимости.


Перефразируя из комментариев:

  1. Дан: «Каковы отрицательные последствия, если это ограничение не выполняется?»

  2. Я: "Тогда пользователь мог бы индексировать с помощью map [t1] и map [t2]. Если бы они были одного типа, не было бы никакого различия, и это не имело бы никакого смысла."

  3. Дэн: Компилятор фактически позволяет [два параметра универсального типа определять различные перегрузки метода], поэтому мне любопытно; произвольно выбирает один из методов для вызова?

Ответы [ 6 ]

4 голосов
/ 09 ноября 2011

Расширение на примере, чтобы выделить проблему:

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.)

0 голосов
/ 09 ноября 2011

Нет, вы не можете использовать равенство (или неравенство) в качестве ограничения. Проще говоря, равенство - это не ограничение, а условие. Вы должны проверить на условие , такое как равенство или неравенство типов в конструкторе, и выдать соответствующее исключение.

0 голосов
/ 09 ноября 2011

Я не совсем уверен, почему это была бы желательная проверка времени компиляции. Было бы возможно, по существу, обойти условие, поместив в скобки либо ключ, либо значение, тем самым сделав проверку времени компиляции бесполезной.

Нужно подумать, чтобы определить ... какие ошибки я пытаюсь предотвратить?

Если вы просто мешаете ленивому сотруднику не читать документацию, добавьте проверку «Только отладка» и сгенерируйте исключение. Таким образом, чек может быть удален для кода выпуска, например

#if Debug

if (T1 is T2 || T2 is T1)
{
    throw new ArguementException(...);
}

#endif

Если вы пытаетесь запретить злонамеренному человеку использовать вашу библиотеку непреднамеренным образом, то, возможно, потребуется проверка во время выполнения, в противном случае было бы легко указать ключ или значение.

0 голосов
/ 09 ноября 2011

Неравенство не поможет компилятору отлавливать ошибки.Когда вы указываете ограничения на параметры типа, вы сообщаете компилятору, что переменные этого типа всегда будут поддерживать определенный интерфейс или будут вести себя определенным образом.Каждый из них позволяет компилятору проверять что-то вроде «этот метод будет присутствовать, поэтому его можно вызывать на T».

Неравенство параметров типа больше похоже на проверку того, что аргументы метода не равны нулю.Это часть логики программы, а не ее тип безопасности.

0 голосов
/ 09 ноября 2011

Нет эффективного способа сделать это без наложения каких-либо других ограничений на сами типы.Как кто-то еще заметил, вы можете наложить ограничения на то, что два типа получены из двух разных базовых классов, но это, вероятно, не очень хорошо с точки зрения дизайна.потому что никто в Microsoft никогда не считал, что что-то подобное необходимо применять во время компиляции, в отличие от других ограничений, связанных с тем, как вы на самом деле можете использовать переменные указанных типов.И, как отмечают некоторые комментаторы, вы, безусловно, можете применить это во время выполнения.

0 голосов
/ 09 ноября 2011

Типовые ограничения кажутся неправильными.Несмотря на то, что они противоречат параметру типа, цель состоит в том, чтобы сообщить компилятору, какие операции доступны для типа.
Если вы хотите, вы можете иметь ограничение, при котором оба T1 и T2 являются производными от отдельных конкретных базовых классов., но я не думаю, что это именно то, что вы хотите.

...