在重写模板方法时访问中间类属性

本文关键字:中间 属性 访问 重写 模板方法 | 更新日期: 2023-09-27 17:57:57

我有一个抽象类,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继承自从GenericModel继承BasicModel。所以我定义了一个覆盖方法(在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 的子类,它们都具有id属性。

有没有办法在ChildModel的覆盖中访问这些中间类属性DoStuff()或者我设置继承的方式是不可能的?我可以进行任何解决方法或更改以允许使用这些属性吗?

编辑:从评论和答案来看,我对模板方法有根本的误解。我真正想做的是在GenericModel中定义一个泛型方法,该方法将IEnumerable<GenericModel>作为输入,然后在每个子类中被重写,其中覆盖使用IEnumerable<ChildClass>代替。显然,以我定义的结构,这个目标可能是不可能的。

在重写模板方法时访问中间类属性

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) { ... }
}

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

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

有了这个:

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

这样,如果您有此示例:

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); 

您的输出将是:

2
10
15

好吧,使用当前逻辑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;
    }
}