C # Общие и переопределить методы - PullRequest
0 голосов
/ 28 октября 2019

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

Я хочу создать алгоритм, который складывает 2 числа, будь то stringили int. Я также хочу иметь возможность выводить все на консоль и хотел бы избежать дублирования кода. Я создаю generic interface, который меняется в зависимости от модели. Затем класс, который обрабатывает добавление двух членов. Обратите внимание, что данные поступают из классов-обработчиков (которые в реальном сценарии получают информацию для заполнения модели из внешних данных).

Обратите внимание, что я знаю, что знаю, что есть намного более простые способы допинга, но я хочу понять, почему это не работает. Теперь код не компилируется вValidateHandler<T>(IAddHandler<T> handler) из-за cannot convert from 'T' to 'Test.Models.StringModel'. Почему он не может выбрать правильный переопределенный метод? Я попытался добавить обобщения в переопределенный метод, но он все еще не работает. Как я могу ValidateHandler<T> выбрать правильный метод из его типа?

Вот код, который я написал.

Модели:

public class IntegerModel
{
    public int A { get; set; }

    public int B { get; set; }

    public int C { get; set; }
}

public class StringModel
{
    public string A { get; set; }

    public string B { get; set; }

    public string C { get; set; }
}

Интерфейс:

public interface IAddHandler<T>
{
    T Add();

    void GetData();
}

Обработчики:

public class IntegerHandler : IAddHandler<IntegerModel>
{
    public IntegerModel IntegerModel { get; set; }

    public void GetData()
    {
        // Get Info to Add from external file for example
        IntegerModel = new IntegerModel { A = 10, B = 20 };
    }

    public IntegerModel Add()
    {
        IntegerModel.C = IntegerModel.A + IntegerModel.B;

        return IntegerModel;
    }
 }

public class StringHandler : IAddHandler<StringModel>
{
    public StringModel StringModel { get; set; }

    public void GetData()
    {
        // Get Info to Add from external file for example
        StringModel = new StringModel { A = "10", B = "20" };
    }

    public StringModel Add()
    {
        StringModel.C = StringModel.A + StringModel.B;
        return StringModel;
    }
 }

Вот Main с его функцией

public static void Main(string[] args)
{
    var integerHandler = new IntegerHandler();
    var stringHandler = new StringHandler();

    ValidateHandler(integerHandler);
    ValidateHandler(stringHandler);
}

public static void ValidateHandler<T>(IAddHandler<T> handler)
{
    handler.GetData();
    var result = handler.Add();

    WriteResults(result);
}

public static void WriteResults(StringModel model)
{
    Console.WriteLine(model.C);
}

public static void WriteResults(IntegerModel model)
{
    Console.WriteLine(model.C);
}

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

public static void ValidateHandler<T>(IAddHandler<T> handler)
{
    handler.GetData();
    var result = handler.Add();

    if (typeof(T) == typeof(StringModel))
    {
        WriteResults(result as StringModel);
    }
    else if (typeof(T) == typeof(IntegerModel))
    {
        WriteResults(result as IntegerModel);
    }
}

1 Ответ

2 голосов
/ 29 октября 2019

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

public interface IResult
{
    Object Result { get; }
}

public interface IModel : IResult
{
    Object A { get; }
    Object B { get; }
}

public class Model<T> : IModel
{
    public T A { get; set; }
    public T B { get; set; }
    public T Result { get; set; }

    //  The "IModel.*" prefix means this is an "explicit" implementation of the IModel 
    //  interface. That means that ordinarily, model.A gets you the above strongly typed
    //  A property, but with an explicit cast to IModel, you get the non-generic IModel 
    //  version: model.A is int or whatever T is; ((IModel)model).A is object. 
    //
    //  These getters don't recurse because this is of type Model<T>, not IModel.
    //  If T is List<String>, a subclass could override this to return 
    //  String.Join(", ", Result);
    Object IModel.A => A;
    Object IModel.B => B;
    Object IResult.Result => Result;
}

public interface IAddHandler<T> where T : IModel
{
    T Model { get; set; }
    T Add();

    void GetData();
}

public class IntegerHandler : IAddHandler<Model<int>>
{
    public Model<int> Model { get; set; }

    public void GetData()
    {
        // Get Info to Add from external file for example
        Model = new Model<int> { A = 10, B = 20 };
    }

    public Model<int> Add()
    {
        Model.Result = Model.A + Model.B;

        return Model;
    }
}

public class StringHandler : IAddHandler<Model<string>>
{
    public Model<string> Model { get; set; }

    public void GetData()
    {
        // Get Info to Add from external file for example
        Model = new Model<string> { A = "10", B = "20" };
    }

    public Model<string> Add()
    {
        Model.Result = Model.A + Model.B;

        return Model;
    }
}

Основной:

class Program
{
    public static void Main(string[] args)
    {
        var integerHandler = new IntegerHandler();
        var stringHandler = new StringHandler();

        ValidateHandler(integerHandler);
        ValidateHandler(stringHandler);
    }

    //  This method must require T to be IModel because IAddHandler<T> requires 
    //  T to be IModel.
    public static void ValidateHandler<T>(IAddHandler<T> handler) where T : IModel
    {
        handler.GetData();
        var result = handler.Add();

        //  IModel inherits from IResult. Hence, anything that implements IModel 
        //  must implement IResult. So this is an implicit cast, even though IResult 
        //  is implemented explicitly. 
        WriteResults(result);
    }

    //  The parameter here could be IModel instead, but maybe there are other non-model 
    //  classes that implement IResult.
    public static void WriteResults(IResult result)
    {
        Console.WriteLine(result.Result);
    }
}
...