Как я могу решить сценарий универсального класса <T>? - PullRequest
1 голос
/ 13 января 2012

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

public interface IProblem<T>
{
    ushort ResultCount { get; }
    T[] Results { get; }

    bool IsCorrect();
}

public abstract class ProblemBase<T> : IProblem<T>
{
    private T[] _results;
    private ushort? _resultCount;

    public ushort ResultCount
    {
        get
        {
            if (_resultCount == null) throw new ArgumentNullException("_resultCount");
            return (ushort)_resultCount;
        }
        protected set
        {
            if (_resultCount != value)
                _resultCount = value;
        }
    }

    public T[] Results
    {
        get
        {
            if (_results == null)
                _results = new T[ResultCount];

            return _results;
        }
    }

    public abstract bool IsCorrect();
}

Это пример, где я создаю арифметическую задачу, которая называется ProblemA.T равно decimal, потому что тип данных массива должен быть десятичным (другие проблемы могут иметь string или int)

public class ProblemA: ProblemBase<decimal>
{
    private decimal _number1;
    private decimal _number2;
    private Operators _operator;

    public decimal Number1
    {
        get { return _number1; }
        set { _number1 = value; }
    }

    public decimal Number2
    {
        get { return _number2; }
        set { _number2 = value; }
    }

    public Operators Operator
    {
        get { return _operator; }
        set { _operator = value; }
    }

    public decimal Result
    {
        get { return Results[0]; }
        set { Results[0] = value; }
    }

    public ProblemA()
    {
        this.ResultCount = 1;
    }

    public override bool IsCorrect()
    {
        bool result;

        switch (_operator)
        {
            case Operators.Addition:
                result = this.Result == (this.Number1 + this.Number2);
                break;
            case Operators.Subtract:
                result = this.Result == (this.Number1 - this.Number2);
                break;
            case Operators.Multiplication:
                result = this.Result == (this.Number1 * this.Number2);
                break;
            case Operators.Division:
                result = this.Result == (this.Number1 / this.Number2);
                break;
            default:
                throw new ArgumentException("_operator");
        }

        return result;
    }
}

Я использую MVVM, поэтому я хотел быесть ViewModel для каждой задачи, где содержится ProblemBase<T> как свойство, но как оно является универсальным, я думаю, это будет проблемой, если положить в IProblemViewModel как универсальное.

public interface IProblemViewModel : IViewModel
{
    ProblemBase<T> Problem { get; set; }
}

Я сказал это потомупозже планируется использовать ObservableCollection<IProblemViewModel>, поэтому я не уверен, что если я напишу IProblemViewModel или IProblemViewModel<T>, не возникнет никаких проблем.Заранее спасибо.

Ответы [ 2 ]

2 голосов
/ 14 января 2012

Может быть, я не совсем понял это, но это то, что вы ищете?

    ObservableCollection<IProblemViewModel<object>> collection = new ObservableCollection<IProblemViewModel<object>>
    {
        new ProblemViewModel<DerivedResult>(),
        new ProblemViewModel<OtherResult>()
    };

Этого можно добиться, объявив общий аргумент ковариантным.

Вы также можете изменить коллекцию на

ObservableCollection<IProblem<BaseType>>

и просто заставьте его принять определенную цепочку результатов. В этом примере DerivedResult и OtherResult должны наследоваться от BaseType, чтобы поместиться в коллекцию.

Большое предостережение в том, что примитивные типы никак не вписываются в эту иерархию. Вам придется обернуть их в IProblem<IntResult> и т. Д.

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

Последнее предостережение: невозможно иметь свойство 'set' для ковариантного типа, поэтому IProblemViewModel может поддерживать только get.

Полный, компилируемый пример:

class Program
{

    public interface IProblem<out T>
    {
        ushort ResultCount { get; }
        T[] Results { get; }

        bool IsCorrect();
    }

    public class ProblemBase<T> : IProblem<T>
    {
        private T[] _results;
        private ushort? _resultCount;

        public ushort ResultCount
        {
            get
            {
                if (_resultCount == null) throw new ArgumentNullException("_resultCount");
                return (ushort)_resultCount;
            }
            protected set
            {
                if (_resultCount != value)
                    _resultCount = value;
            }
        }

        public T[] Results
        {
            get
            {
                if (_results == null)
                    _results = new T[ResultCount];

                return _results;
            }
        }

        public bool IsCorrect()
        {
            return true;
        }
    }

    public interface IProblemViewModel<out T>
    {
        IProblem<T> Problem { get; }
    }

    public class BaseResult
    {

    }

    public class DerivedResult : BaseResult
    {

    }

    public class OtherResult : BaseResult
    {

    }

    public class ProblemViewModel<T> : IProblemViewModel<T>
    {

        public IProblem<T> Problem
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }
    }


    static void Main(string[] args)
    {
        ObservableCollection<IProblemViewModel<object>> collection = new ObservableCollection<IProblemViewModel<object>>
        {
            new ProblemViewModel<DerivedResult>(),
            new ProblemViewModel<OtherResult>()
            //, new ProblemViewModel<int>()   // This is not possible, does not compile.
        };
    }
}
0 голосов
/ 13 января 2012

Ваш интерфейс модели представления может быть определен следующим образом:

public interface IProblemViewModel<T> : IViewModel
{
    //No reason to use the base here instead of the interface
    IProblem<T> Problem { get; set; }
}

Я не уверен, планируете ли вы связать проблему с интерфейсом в WPF или Silverlight, но если вы уверены, чтоПроблема также реализует INotifyPropertyChanged .Привязка к не зависимым свойствам на объектах, которые не реализуют INotifyPropertyChanged, вызывает утечку памяти, когда объект никогда не будет освобожден.Вы можете найти больше информации об утечке здесь: http://support.microsoft.com/kb/938416

РЕДАКТИРОВАТЬ: Добавлен ответ на комментарий.

Вы правы, что наличие IProblemViewModel<T> остановит вас использовать его в ObservableCollection, еслинамереваться показать более одного типа <T>.Однако, поскольку при связывании не имеет значения тип объекта при его привязке, почему бы просто не сделать коллекцию ObservableCollection<IViewModel>?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...