Общий класс для автоматического создания конкретного класса

Есть ли способ взять интерфейс, скажем:

/// <summary>
/// Interface containing operators which operate on T
/// </summary>
public interface IScalarOperators<T>
{
    // Adds two T objects
    IOperateScalar<T> OperatorAdd { get; }
    // Subtracts two T objects
    IOperateScalar<T> OperatorSubtract { get; }
    // Multiplies two T objects
    IOperateScalar<T> OperatorMultiply { get; }
}

// Class containing all the Scalar operators for a given T
class ScalarOperators<T> : IScalarOperators<T>
{
    public IOperateScalar<T> OperatorAdd { get; private set; }
    public IOperateScalar<T> OperatorSubtract { get; private set; }
    public IOperateScalar<T> OperatorMultiply { get; private set; }
    private ScalarOperators(IOperateScalar<T> add, IOperateScalar<T> subtract, IOperateScalar<T> multiply)
    {
        this.OperatorAdd = add;
        this.OperatorSubtract = subtract;
        this.OperatorMultiply = multiply;
    }

    public static ScalarOperators<bool> CreateBool()
    {
        return new ScalarOperators<bool>(new AddBool(), new SubtractBool(), new MultiplyBool());
    }

    public static ScalarOperators<int> CreateInt()
    {
        return new ScalarOperators<int>(new AddInt(), new SubtractInt(), new MultiplyInt());
    }

    // METHOD I WANT TO ADD
    public static ScalarOperators<T> Create()
    {
      // if T == bool
      // return CreateBool()
      // if T == int
      // return CreateInt()
      // else (no definition available for T)
      // return null
    }

    // I tried something like below, but it didn't work...
    public static ScalarOperators<T> Create<T>() where T: bool
    { return CreateBool(); }

    public static ScalarOperators<T> Create<T>() where T : int
    { return CreateInt(); }

    public static ScalarOperators<T> Create<T>()
    { return null; }
}

Обратите внимание, я бы хотел использовать универсальный метод Create, который создает правильный набор операторов, но я не уверен, как это сделать.

Я хотел бы использовать его для удаления параметра из этого метода:

    public static IMatrix<T> Add<T>(this IMatrix<T> matrix, IMatrix<T> other, IScalarOperators<T> operators)
    {
        JoinCells<T> joiner = new JoinCells<T>();
        return joiner.Join(matrix, other, null, operators.OperatorAdd);
    }

становится

    public static IMatrix<T> Add<T>(this IMatrix<T> matrix, IMatrix<T> other)
    {
        JoinCells<T> joiner = new JoinCells<T>();
        return joiner.Join(matrix, other, null, ScalarOperators<T>.Create().OperatorAdd);
    }

Спасибо за любую помощь! В основном, я просто не хочу передавать объект scalarOperator методу расширения, я бы предпочел иметь "default", поскольку маловероятно, что ScalarOperators изменится для любого T, который определен.

3 ответа

Я предлагаю создать фабрику IScalarOperators вместо статического класса (если вам действительно нужно, чтобы он был статическим, вы могли бы получить к нему доступ через статическое поле). Вы можете зарегистрировать их при запуске приложения и получить их по методу этого примера:

 public IScalarOperators<T> Create<T>()
 {
    // check if exists in dictionary
    return (ScalarOperators<T>)dict[typeof(T)];
 }

dict будет иметь тип словаря. Преимущество состоит в том, что вы можете добавлять новые IScalarOperators во время роста приложения, только создав новый реализующий класс и зарегистрировав его на фабрике, приведение является недостатком. Также у вас будет лучшее разделение проблем и (на мой взгляд) более чистый код.

Что вам нужно сделать, это получить тип T.

Ваш метод Create может быть таким:

public static ScalarOperators<T> Create()
{
    Type type = typeof(T); 
    if(type == typeof(bool))
      return CreateBool()
    if(type == typeof(int))
      return CreateInt()
    else
      return null
}

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

Для первого вы всегда будете использовать одни и те же операторы для одного и того же типа (по крайней мере, вы никогда не будете пытаться использовать операторы bool для типа int). Нет причин усложнять вещи, имея отдельный класс для них. Что касается последнего, универсальные классы и универсальные методы должны работать одинаково для любого заданного T. Конечно, вы очень хорошо могли бы получить typeof(T) в вашем методе статической фабрики и сравните с этим для нескольких конкретных случаев, а затем вам придется изменить это для каждого нового T, который вы хотите обработать из-за этой слишком сложной общей структуры операндов.

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

public interface IScalarOperators<T>
{
    IScalarOperators<T> Add (IScalarOperators<T> rightSide);
    IScalarOperators<T> Subtract (IScalarOperators<T> rightSide);
    IScalarOperators<T> Multiply (IScalarOperators<T> rightSide);
    T Unwrap();
}

public interface IMatrix<T> where T : IScalarOperators<T> { /* whatever */ }

public class CustomInt : IScalarOperators<CustomInt>
{
    private readonly int number;
    public CustomInt(int number) { this.number = number; }
    public CustomInt Unwrap() { return this; }
    public IScalarOperators<CustomInt> Add(IScalarOperators<CustomInt> rightSide) { return new CustomInt(number + rightSide.Unwrap().number); }
    public IScalarOperators<CustomInt> Subtract(IScalarOperators<CustomInt> rightSide) { return new CustomInt(number - rightSide.Unwrap().number); }
    public IScalarOperators<CustomInt> Multiply(IScalarOperators<CustomInt> rightSide) { return new CustomInt(number * rightSide.Unwrap().number); }
}

На этом этапе вы можете работать на IMatrix<CustomInt> сквозь IScalarOperators<T> интерфейс и выполнять любые открытые операции, которые вы хотите. В качестве грубого примера, предположим, что у вас есть открытый метод доступа array, ты мог бы сказать IScalarOperators<T> result = matrix.array[0, 0].Add(matrix.array[0, 1]); и получить представление о сложении двух вместе. Затем вы можете выполнить любые дальнейшие операции над этим и так далее.

Другие вопросы по тегам