Patrick Skelton Ответов: 1

Как я могу использовать универсальный интерфейс, с помощью шаблона "фабрика"?


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

Я не вижу ничего концептуально неправильного в том, что я пытаюсь сделать, но, похоже, у меня полная путаница с синтаксисом. Конечно, если то, что я пытаюсь сделать, по сути своей плохая идея, я всегда готов учиться у более великих умов.

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

public interface IReturnsValue<T>
{
    void SetValues( List<T> listOfValues );
    T NextValue { get; }
}

public class IntegerReturner : IReturnsValue<int>
{
    public void SetValues( List<int> listOfValues ) { _values = listOfValues; }
    public int NextValue => _values[ _indexOfNextValue++ ];
    private int _indexOfNextValue = 0;
    private List<int> _values = null;
}

public class DoubleReturner : IReturnsValue<double>
{
    public void SetValues( List<double> listOfValues ) { _values = listOfValues; }
    public double NextValue => _values[ _indexOfNextValue++ ];
    private int _indexOfNextValue = 0;
    private List<double> _values = null;
}

public class ValueReturnerFactory
{
    public IReturnsValue<T> CreateValueReturner<T>( string name )
    {
        if( name == "IntegerReturner" )
            return new IntegerReturner();
        if( name == "DoubleReturner" )
            return new DoubleReturner();
        throw new ArgumentException();
    }
}

class Program
{
    static void Main( string[] args )
    {
        ValueReturnerFactory factory = new ValueReturnerFactory();
        IReturnsValue<T> firstValueReturner = factory.CreateValueReturner<int>( "IntegerReturner" );
        IReturnsValue<T> secondValueReturner = factory.CreateValueReturner<double>( "DoubleReturner" );
    }
}


Что я уже пробовал:

Каждая комбинация параметров общего типа кода, которые я могу придумать. :(

1 Ответов

Рейтинг:
10

lmoelleb

Это не работает, так как компилятор не может проверить, всегда ли T является int при использовании IntegerReturner и т. д. Например, следующий вызов будет действителен в соответствии с сигнатурой метода:

factory.CreateValueReturner<bool>("IntegerReturner")

куда он должен был бы вернуться IReturnsValue<int> в качестве IReturnsValue<bool>.

Вы можете "взять на себя ответственность" и бросить на заводе. Так:
public IReturnsValue<T> CreateValueReturner<T>(string name)
{
    if (name == "IntegerReturner")
        return (IReturnsValue<T>)new IntegerReturner();
    if (name == "DoubleReturner")
        return (IReturnsValue<T>)new DoubleReturner();
    throw new ArgumentException();
}

затем вы получите исключение времени выполнения, если типы не совпадают.

Вы также должны указать тип в основном методе, так как T здесь не определен (или использовать var конечно):
IReturnsValue<int> firstValueReturner = factory.CreateValueReturner<int>("IntegerReturner");

IReturnsValue<double> secondValueReturner = factory.CreateValueReturner<double>("DoubleReturner");

Трудно сказать, есть ли лучшие способы реализовать это, это зависит от остальной части кода и личных предпочтений. Если бы это был я, я бы подумал, что могу избавиться от имени и выбрать класс для создания экземпляра из типа T - тогда у вас не может быть ситуации, когда T и имя не совпадают. Фабричный метод может быть примерно таким:
public IReturnsValue<T> CreateValueReturner<T>()
{
    if (typeof(T) == typeof(int))
        return (IReturnsValue<T>)new IntegerReturner();
    if (typeof(T) == typeof(double))
        return (IReturnsValue<T>)new DoubleReturner();
    throw new ArgumentException();
}