Универсальный класс для автоматического создания конкретного класса

#c# #.net #generics #default-implementation

#c# #.net #общие #по умолчанию -реализация

Вопрос:

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

 /// <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 в метод расширения, я бы предпочел иметь значение «по умолчанию», поскольку маловероятно, что ScalarOperators изменится для любого определенного T .

Комментарии:

1. Похоже, вы пытаетесь создать библиотеку, позволяющую выполнять общие вычисления? Возможно, вас заинтересует эта библиотека . Совсем недавно деревья выражений предложили другое решение, которое также выглядит действительно красиво. Ознакомьтесь с реализацией MiscUtils Марка Гравелла.

Ответ №1:

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

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

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

Ответ №2:

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

Ваш метод создания может быть таким:

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

Ответ №3:

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

В первом случае вы всегда будете использовать одни и те же операторы для одного и того же типа (по крайней мере, вы никогда не будете пытаться использовать операторы 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); }
}
 

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