在覆盖模板方法

时间:2016-07-20 15:51:51

标签: c#

我有一个抽象类GenericModel,它定义了一个模板方法,如下所示:

public virtual string DoStuff<TType>(IEnumerable<TType> input)
    where TType : GenericModel { return null; }

然后我有一个继承自GenericModel BasicModel的类,它代表GenericModel的某个子集,它们有足够的共同点来保证另一个结构。虽然这是一个具体的类,但它实际上从未实例化,而是其他类继承它,因为有许多共同的属性不一定在每个GenericModel中,但在每个BasicModel中(例如,我们会说每个BasicModel都有一个id属性)。

如果我的某个类继承自BasicModel ChildModel,并且我想覆盖DoStuff(),那么这是允许的,因为ChildModel继承自BasicModel继承自GenericModel的。所以我定义了一个覆盖方法(在ChildModel中),如下所示:

public override string DoStuff<ChildModel>(IEnumerable<ChildModel> input){
    foreach(var data in input){
        var foo = data.id;
        //Do stuff with foo
    }
    //Do more stuff
}

问题是Visual Studio将data.id高亮显示为错误,称data没有名为id的属性。我的假设是这是由模板方法的where TType : GenericModel部分引起的;它会忽略中间BasicModel,因此我无法访问BasicModel中定义的属性。

这对我来说很奇怪,因为我说输入必须ChildModel组成,根据定义,BasicModel的子类都有{ {1}}属性。

有没有办法在id的{​​{1}}覆盖中访问这些中间类属性,或者我设置继承的方式是不可能的?是否有任何变通方法或变更可以允许使用这些属性?

编辑:从评论和答案中可以看出,我对模板方法存在根本性的误解。我真正要做的是在ChildModel中定义一个泛型方法,该方法将DoStuff()作为输入,然后在每个子类中重写,其中覆盖使用GenericModel代替。显然,我已经定义的结构可能无法实现这一目标。

3 个答案:

答案 0 :(得分:2)

public override string DoStuff<ChildModel>(IEnumerable<ChildModel> input) { ... }

不会将DoStuff专门用于仅使用ChildModel,而只是将TType参数重命名为ChildModel。这相当于

public override string DoStuff<T>(IEnumerable<T> input) { }

这不会编译,因为你还需要在基类的TType上保留约束。

DoStuff方法的泛型约束意味着覆盖必须统一处理所有子类型。听起来您希望每个子类都支持GenericModel的单个特定子类型。在这种情况下,您可以将泛型参数移动到基类,并在每个子类中指定它,例如

public abstract class BaseClass<TType> where TType : GenericModel {
    public virtual string DoStuff(IEnumerable<TType> input) { ... }
}

public class SubClass : BaseClass<ChildModel> {
    public override string DoStuff(IEnumerable<ChildModel> input) { ... }
}

答案 1 :(得分:1)

嗯,使用当前逻辑ChildModel类型参数只能是GenericModel类型,因为无法覆盖泛型类型约束。 因此 - 编译器无法知道我们实际上将可转换的内容传递给BasicModel,这就是我们无法看到属性'id'的原因。

要将其用作BasicModel,我们可以使用LINQ投射整个集合:

class GenericModel
{
    public virtual string DoStuff<TType>(IEnumerable<TType> input)
        where TType : GenericModel
    {
        return null;
    }
}

class BasicModel : GenericModel
{
    public int id { get; set; }
}

class ChildModel : BasicModel
{
    public override string DoStuff<TType>(IEnumerable<TType> input)
    {
        var castedInput = input.Cast<BasicModel>();

        foreach (var data in castedInput)
        {
            var foo = data.id;
            //Do stuff with foo
        }
        //Do more stuff

        return null;
    }
}

答案 2 :(得分:1)

如果你不想要CastException(@Fabjan回答),你可以替换它:

componentsSeparatedByString

用这个:

var castedInput = input.Cast<BasicModel>();  
foreach (var data in castedInput) 

如果您有这个例子:

foreach (var data in input.OfType<BasicModel>())

使用DoStuff:

new ChildModel().DoStuff(new List<GenericModel>
{
   new GenericModel(),
   new BasicModel{id = 2},
   new ChildModel{id = 10},
   new BasicModel{id = 15}
});

您的输出将是:

var foo = data.id;
Console.WriteLine(foo);