不确定何时在C#中使用'base'

Sup*_*gel 3 c# oop abstract-class interface base

我正在尝试自学C#中的OOP,但是我有一个关于何时使用的问题base。我了解一般原则,但不确定以下示例中的最佳方法。这个简单的测试包括:

  • 一个interface具有两个string属性
  • 一个abstract实现此接口并添加更多string属性的类
  • 实现抽象类的两个类。一个使用base,另一个不使用,但是在执行程序时它们都产生相同的输出。

我的问题是:在此示例中,一种实现是否比另一种实现更理想?我不太确定TranslationStyleA和之间是否存在任何有意义的区别TranslationStyleB,还是仅取决于个人喜好?

非常感谢您的时间和想法!

using System;

namespace Test
{
    interface ITranslation
    {
        string English { get; set; }
        string French { get; set; }
    }

    public abstract class Translation : ITranslation
    {
        public virtual string English { get; set; }
        public virtual string French { get; set; }

        public string EnglishToFrench { get { return English + " is " + French + " in French"; } }
        public string FrenchToEnglish { get { return French + " is " + English + " in English"; } }

        public Translation(string e, string f)
        {
            English = e;
            French = f;
        }
    }

    public class TranslationStyleA : Translation
    {
        public override string English
        {
            get { return base.English; }
            set { base.English = value; }
        }

        public override string French
        {
          get { return base.French; }
          set { base.French = value; }
        }

        public TranslationStyleA(string e, string f) : base(e, f)
        {
        }
    }

    public class TranslationStyleB : Translation
    {
        private string english;
        public override string English
        {
            get { return english; }
            set { english = value; }
        }

        private string french;
        public override string French
        {
            get { return french; }
            set { french = value; }
        }

        public TranslationStyleB(string e, string f) : base(e, f)
        {
            this.English = e;
            this.French = f;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            TranslationStyleA a = new TranslationStyleA("cheese", "fromage");
            Console.WriteLine("Test A:");
            Console.WriteLine(a.EnglishToFrench);
            Console.WriteLine(a.FrenchToEnglish);

            TranslationStyleB b = new TranslationStyleB("cheese", "fromage");
            Console.WriteLine("Test B:");
            Console.WriteLine(b.EnglishToFrench);
            Console.WriteLine(b.FrenchToEnglish);

            Console.ReadKey();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Chr*_*ain 5

您需要了解的第一件事是拥有自动属性时发生的情况:

public virtual string English { get; set; }
Run Code Online (Sandbox Code Playgroud)

在后台,编译器将生成一个私有字段,并在您访问该属性时获取/设置该私有字段。相当于这个

private string _english;
public virtual string English { get { return _english; } set { _english = value; } }
Run Code Online (Sandbox Code Playgroud)

除了您不知道私有字段的名称之外,因此您无法访问它。

因此,在您的TranslationStyleA类中,您实际上并没有对English属性做任何事情,因为它只是直接访问基类的属性,而不会改变其行为。

    // None of this is even needed- we are just delegating to the base class
    public override string English
    {
        get { return base.English; }
        set { base.English = value; }
    }
Run Code Online (Sandbox Code Playgroud)

现在在TranslationStyleB类中,您实际上是在更改属性的行为(尽管以相当无用的方式)。您无需将English属性的值存储在基类的自动实现的私有变量中,而是将其存储在派生类级别定义的私有变量中:

    private string english;
    public override string English
    {
        get { return english; }
        set { english = value; }
    }
Run Code Online (Sandbox Code Playgroud)

这些实现当然都不会做任何事情,并且不需要实现,因为基类本身会很好地实现属性。因此,对于您的原始问题,我的回答是,鉴于您所描述的代码,两者都不是首选。

现在,让我们看一个与您的问题相关的示例。例如,仅在要更改其行为时才需要覆盖它们。

    // We don't want any leading or trailing whitespace, so we remove it here.
    public override string English
    {
        get { return base.English; }
        set { base.English = value.Trim(); }
    }
Run Code Online (Sandbox Code Playgroud)

我们想在这里委托给基类,因为为什么这些首先是属性。在语义上,属性与字段相同:

public String Foo;
public String Foo { get; set; } // <-- why bother with all this extra { get; set; } stuff?
Run Code Online (Sandbox Code Playgroud)

原因是从编译器的角度来看,从属性到字段是接口的重大变化。所以如果我改变

public String Foo;
Run Code Online (Sandbox Code Playgroud)

public String Foo { get; set; }
Run Code Online (Sandbox Code Playgroud)

然后,任何依赖于我的代码的代码都需要重新编译。但是,如果我改变

public String Foo { get; set; }
Run Code Online (Sandbox Code Playgroud)

private string _foo;
public String Foo { get { return _foo; } set { _foo = value.Trim(); } }
Run Code Online (Sandbox Code Playgroud)

那么从属代码仍然只能看到公共属性,并且不需要重新编译(因为我的类的接口未更改)。

如果此处(Translation)的基类要更改,则该属性的行为English如下:

private string _english;
public String English { get { return _english; } set { _english = value.ToUpper(); } }
Run Code Online (Sandbox Code Playgroud)

您可能想在派生类中学习!

因此,考虑到属性具有与之关联的行为,您应该始终委托给父类实现,除非该实现对派生类产生不良影响。