【问题标题】:How can I resolve a generic class <T> scenario?如何解决泛型类 <T> 方案?
【发布时间】:2012-01-12 23:22:13
【问题描述】:

我在使用泛型时遇到了问题。我正在创建一个名为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();
}

这是我创建算术问题的示例,称为ProblemATdecimal 因为数组数据类型应该是十进制(其他问题可能有 stringint

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,所以我想为每个包含 ProblemBase&lt;T&gt; 作为属性的问题创建一个 ViewModel,但是它是通用的,我想如果将 IProblemViewModel 作为通用的。

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

我这么说是因为后来打算用ObservableCollection&lt;IProblemViewModel&gt;,所以我不确定我写IProblemViewModelIProblemViewModel&lt;T&gt;是否没有问题。 提前致谢。

【问题讨论】:

  • 顺便说一下,不要留下一个抛出 NotImplementedException() 等待其他地方的一些错误无法覆盖它的实现。执行public abstract bool IsCorrect();,就抽象类而言,您实现了接口,但任何未能覆盖它的具体派生类都不会编译。
  • @JonHanna 感谢乔恩的提示,我还在学习它!我会记住的

标签: c# class mvvm interface


【解决方案1】:

也许我没有完全理解这一点,但这就是你所追求的吗?

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

这可以通过将泛型参数声明为协变来实现。

您也可以将集合更改为

ObservableCollection<IProblem<BaseType>>

并让它接受特定的结果链。在此示例中,DerivedResult 和 OtherResult 必须从 BaseType 继承以适合集合。

需要注意的是,原始类型无论如何都不适合这种层次结构。您必须将它们包装在 IProblem&lt;IntResult&gt; 等中。

当然,您可以实现一个简单的载体,例如 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.
        };
    }
}

【讨论】:

    【解决方案2】:

    您的视图模型接口可以这样定义:

    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

    编辑:添加了评论的答案。

    如果您打算显示不止一种类型的&lt;T&gt;,那么拥有IProblemViewModel&lt;T&gt; 会阻止您在ObservableCollection 中使用它是正确的。但是,既然当你绑定时,绑定到它时对象类型是什么并不重要,为什么不直接将集合设为ObservableCollection&lt;IViewModel&gt;

    【讨论】:

    • 感谢您的建议,我真的不知道这个问题。对了,我忘了加注:我打算有一个 ObservableCollection,我想这是不可能的。
    • 我已经更新了我的答案以尝试帮助解决 ObservableCollection 问题。
    • 是的,我想过,我想我可以做到这一点.. public interface IProblemViewModel : IProblemViewModel { IProblem Problem { get;放; } } 公共接口 IProblemViewModel : IViewModel { } 为此:ObservableCollection,你怎么看?
    • 这是另一种选择,但这取决于 IProblemViewModel 在集合中为您提供的价值。因此,这将取决于您的 ViewModel 代码是否需要从集合中大量读取。
    猜你喜欢
    • 2013-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-22
    • 1970-01-01
    • 2015-11-20
    • 1970-01-01
    • 2011-06-22
    相关资源
    最近更新 更多