【问题标题】:Force property assignment in child classes在子类中强制属性分配
【发布时间】:2011-11-15 10:08:16
【问题描述】:

我有一个抽象的不可变基类,它定义了强制初始化子类,因此抽象调用而不是接口:

public abstract BaseLookup<TPoint, TItem>
{
    protected IEnumerable<TItem> items = null;

    protected BaseLookup(IEnumerable<TItem> items)
    {
        this.items = items;
        this.Initialize();
    }

    public abstract void Initialize();

    // problem deciding which one

    // either implementing a method...
    public abstract TItem GetItem(TPoint point);

    // ...or assigning a method
    public Func<TPoint, TItem> GetItem { get; protected set; }
}

GetItem 执行必须尽可能快。在初始化阶段,我必须检查初始项目并决定GetItem 方法应该做什么。它可以是基于这组项目的众多实现之一。

由于GetItem 方法必须尽可能快,因此将其定义为属性并为其分配一个直接的无分支 lambda 表达式似乎要好得多。但是使用上述定义的子类不会强制为其设置任何值,因此实现者可能会创建一个无效的子类。在属性上定义abstract 访问器将强制他们实现在语义上与实现方法相同的属性。这不会强制执行属性分配。

但是,如果我将其实现为重写的抽象方法,则特定方法将需要包含所有基于项目分支的分支。这意味着每次我调用该方法时都会评估这些分支,从而使其变慢(呃)。

我真正在寻找的是一种强制子类实现者设置GetItem 属性的方法。

我该怎么做?

还要考虑到这个类将被初始化一次,然后被多次使用。用于调用GetItem 方法。

一个简化的示例类(使用属性)

public class IteratorLookup<TPoint, TItem> : BaseLookup<TPoint, TItem>
{
    private TItem single = null;

    public IteratorLookup(IEnumerable<TItem> items) : base(items);

    public override void Initialize()
    {
        if (this.items != null && this.items.Count > 0)
        {
            if (this.items.Count > 1)
            {
                this.GetItem = point => this.items[this.GetIndex(point)];
            }
            else
            {
                this.GetItem = irrelevant => this.single;
            }
        }
        else
        {
            this.GetItem = irrelevant => null;
        }
    }

    private int GetIndex(TPoint point) { ... }
}

简化的示例类(使用方法)

public class IteratorLookup<TPoint, TItem> : BaseLookup<TPoint, TItem>
{
    private TItem single = null;

    public IteratorLookup(IEnumerable<TItem> items) : base(items);

    public override void Initialize()
    {
        // implementing minor speed up
        if (this.items != null && this.items.Count == 1)
        {
            this.single = items[0];
        }
    }

    public override TItem GetItem(TPoint point)
    {
        if (this.items != null && this.items.Count > 0)
        {
            if (this.items.Count > 1)
            {
                return this.items[this.GetIndex(point)];
            }
            return this.single;
        }
        return null;
    }

    private int GetIndex(TPoint point) { ... }
}

【问题讨论】:

  • (1) 通常应该避免在构造函数中调用抽象成员。 (2) 有没有实测过分支版和非分支版的时间差?我假设调用委托比执行分支慢。 --> 对我来说这听起来像是过早的优化。
  • 除了我刚才的评论,我真的不明白你想做什么:-) 有人用某个TPoint 实例调用GetItem。现在您要执行某个功能。对GetItem 的每次调用都具有相同的功能吗?并且这个功能是在Initialize 中根据传入构造函数的项目来选择的?
  • @DanielHilgarth:应该避免调用抽象成员是的,但无论如何它们都会有一个实现,因为否则代码将无法编译。根据您的 cmets,我通过提供更多代码来编辑我的问题以了解我的意思...
  • 我不知道您为什么认为调用abstract 方法与调用virtual 方法有什么不同。调用virtual 方法(以及就此而言abstract 方法)的问题是派生类的构造函数尚未执行,因此您正在处理未完全实例化的对象!有关更多信息,请参阅here
  • 顺便说一句:你没有回答我关于性能比较的问题。你有没有把你发布的代码和方法中直接包含分支的版本进行比较?

标签: c# properties abstract-class


【解决方案1】:

由于 GetItem 方法必须尽可能快,因此将其定义为属性并为其分配一个直接的无分支 lambda 表达式似乎要好得多

是什么让您认为它比方法更快?抽象方法是要走的路。这是一种更自然的方法,并且不存在属性可能未初始化的问题。

但是,如果我将其实现为重写的抽象方法,则特定方法将需要包含所有基于项目分支的分支。这意味着每次我调用该方法时都会评估这些分支,从而使其变慢(呃)。

不确定您的意思...是什么阻止您在GetItem 方法中放入与在GetItem 属性中放入Func&lt;TPoint, TItem&gt; 的代码相同的代码?

附带说明一下,在构造函数中调用虚方法 (Initialize) 时,您可能应该三思而后行,这可能会导致意想不到的问题:基构造函数在派生构造函数之前执行,但它始终是最派生的将被调用的Initialize 的实现;所以如果Initialize的派生实现依赖于派生类构造函数中初始化的东西,它将失败,因为它们还没有被初始化。

【讨论】:

  • first 我的Initialize 是抽象的而不是虚拟的,这使它更安全,second 看看我添加的两个例子来解释一下你的意思'感到困惑。使用属性而不是方法使我的 GetItem 方法调用无分支。只是简单的回报。
  • @RobertKoritnik:关于调用 abstract 方法比在 ctor 中调用 virtual 方法更安全,请参阅我对您问题的评论。这不是真的。
  • @RobertKoritnik,一个抽象方法虚拟的:它不仅可以被覆盖,而且必须被覆盖......
【解决方案2】:

您需要为您的问题注入一些方面。属性非常简单,您无法确保子类是否已调用它。

您利用GetItem 方法将其从abstract 变为virtual,您的基类实现将抛出exception

public virtual TItem GetItem(TPoint point)
{
    throw new Exception("Please implement GetItem method");
}

现在,任何没有覆盖该方法的子类都将陷入异常,并确保子类实现GetItem方法。

【讨论】:

  • 当我可以将其标记为abstract 并强制他们在开发过程中实现它时,为什么我会强制实现者通过抛出异常(这会炸毁他们的应用程序)来实现它,因为他们的代码没有实现就无法编译???
  • 当您希望开发人员实现一个方法时,将该方法标记为abstractvirtual 更好。当然,除非您确实需要实现一些基本功能。但即使在这种情况下,您也可以通过调用抽象的私有实现方法来缓解问题。
  • 你真的应该避免做这样的事情。 (1) 使用NotImplementedException。 (2) 不要这样做。正如罗伯特指出的那样,只需将其设为 abstract!
猜你喜欢
  • 2017-01-13
  • 1970-01-01
  • 2021-04-02
  • 2020-04-04
  • 1970-01-01
  • 2012-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多