Подтвердить что ты не робот

Интерфейс с общим параметром vs Интерфейс с общими методами

Скажем, у меня такой интерфейс и конкретная реализация

public interface IMyInterface<T>
{
    T My();
}

public class MyConcrete : IMyInterface<string>
{
    public string My()
    {
        return string.Empty;
    }
}

Итак, я создаю реализацию MyConcrete для strings, у меня может быть еще одна конкретная реализация для int. И это нормально. Но позвольте сказать, что я хочу сделать то же самое, но с помощью общих методов, поэтому у меня есть

public interface IMyInterface2
{
    T My<T>();
}

public class MyConcrete2 : IMyInterface2
{
    public string My<string>()
    {
        throw new NotImplementedException();
    }
}

Итак, у меня есть тот же IMyInterface2, но который определяет общее поведение с помощью T My<T>(). В моем конкретном классе я хочу реализовать поведение My, но для конкретного типа данных - string. Но С# не позволяет мне это делать.

Мой вопрос, почему я не могу этого сделать? Другими словами, если я могу создать конкретную реализацию MyInterface<T> как MyClass : MyInterface<string> и остановить обобщенность в этой точке, почему я не могу сделать это с помощью общего метода - T My<T>()?

4b9b3361

Ответ 1

Ваша реализация универсального метода также должна быть универсальной, поэтому она должна быть:

public class MyConcrete2 : IMyInterface2
{
    public T My<T>()
    {
        throw new NotImplementedException();
    }
}

Почему ты не можешь сделать My<string>() здесь? Поскольку контракту интерфейса требуется метод, его можно вызывать с любым параметром типа T, и вы должны выполнить этот контракт.

Почему вы не можете остановить универсальность в этом пункте? Потому что это может привести к следующим ситуациям:

Объявления класса:

public interface IMyInterface2
{
    T My<T>(T value);
}

public class MyClass21 : IMyInterface2
{
    public string My<string>(string value) { return value; }
}

public class MyClass22 : IMyInterface2
{
    public int My<int>(int value) { return value; }
}

Использование:

var item1 = new MyClass21();
var item2 = new MyClass22();

// they both implement IMyInterface2, so we can put them into list
var list = new List<IMyInterface2>();
list.Add(item1);
list.Add(item2);

// iterate the list and call My method
foreach(IMyInterface2 item in list)
{
    // item is IMyInterface2, so we have My<T>() method. Choose T to be int and call with value 2:
    item.My<int>(2);

    // how would it work with item1, which has My<string> implemented?
}

Ответ 2

Поскольку ваш интерфейс объявляет общий метод T My<T>(), но вы не реализуете функцию с этой конкретной сигнатурой.

Чтобы добиться того, чего вы хотите, вам необходимо предоставить вместо этого общий параметр T в интерфейсе в первом примере:

public interface IMyInterface2<T>
{
        T My();
}

public class MyConcrete2 : IMyInterface2<string>
{
    public string My()
    {
        throw new NotImplementedException();
    }
}

Ответ 3

когда вы пишете Общий метод, определение предназначено для хранения заполнителя. Фактический тип вводится в изображение при вызове метода. поэтому вместо этого вы должны написать

public T My<T>()
{
    throw new NotImplementedException();
}

и когда вы вызываете метод, вы можете использовать там строку.

Ответ 4

Ваше решение не работает по двум причинам.

Во-первых, интерфейс - это контракт. Когда вы реализуете IMyInterface2, вы гарантируете, что вы реализуете функцию с именем My, которая принимает параметр универсального типа и возвращает этот тип. MyConcrete2 не делает этого.

Во-вторых, генераторы С# не допускают никакой специализации параметров типа. (Я бы хотел, чтобы С# поддерживал это.) Это обычная вещь в шаблонах на С++, где ваш пример будет компилироваться, но любое использование MyConcrete2 не сможет скомпилироваться, если они не назовут My с помощью string.