3

C# で OOP について独学しようとしていますが、いつ使用するかについて質問がありますbase。一般的な原則は理解していますが、以下の例では何が最適かわかりません。この簡単なテストには以下が含まれます。

  • interface2 つのプロパティstringを持つ
  • このインターフェイスを実装し、さらにいくつかのプロパティabstractを追加するクラスstring
  • 抽象クラスを実装する 2 つのクラス。1 つは使用baseし、もう 1 つは使用しませんが、プログラムの実行時にどちらも同じ出力を生成します。

私の質問は次のとおりです。この例では、ある実装が他の実装よりも望ましいですか? 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();
        }
    }
}
4

5 に答える 5

5

最初に理解する必要があるのは、自動プロパティがある場合に何が起こっているかです。

public virtual string English { get; set; }

舞台裏では、コンパイラはプライベート フィールドを生成し、プロパティにアクセスするときにそのプライベート フィールドを取得/設定しています。これに相当します

private string _english;
public virtual string English { get { return _english; } set { _english = value; } }

ただし、プライベート フィールドの名前がわからないため、アクセスできません。

したがって、TranslationStyleAクラスでは、基本クラスのプロパティに直接アクセスするだけで、その動作を変更しないため、英語のプロパティで実際には何もしていません。

    // 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; }
    }

クラスでは、実際にプロパティの動作を変更していますTranslationStyleB(かなり役に立たない方法ではありますが)。基本クラスの自動実装されたプライベート変数に English プロパティの値を格納する代わりに、派生クラス レベルで定義されたプライベート変数に値を格納しています。

    private string english;
    public override string English
    {
        get { return english; }
        set { english = value; }
    }

もちろん、これらの実装はどちらも何もしません。基本クラスはそれ自体で完全にプロパティを実装するため、実装されている場合はどちらも必要ありません。したがって、元の質問に対する私の答えは、あなたが説明したコードを考えると、どちらも好まれないということです。

それでは、あなたの質問が関連している例を見てみましょう。たとえば、動作を変更したい場合にのみオーバーライドする必要があります。

    // 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(); }
    }

そもそもこれらがプロパティであった理由から、ここでは基底クラスにデリゲートしたいと考えています。意味的には、プロパティはフィールドと同じです。

public String Foo;
public String Foo { get; set; } // <-- why bother with all this extra { get; set; } stuff?

その理由は、コンパイラの観点からは、プロパティからフィールドに移行することはインターフェイスの重大な変更であるためです。だから私が変われば

public String Foo;

public String Foo { get; set; }

次に、私のコードに依存するすべてのコードを再コンパイルする必要があります。しかし、私が変わると

public String Foo { get; set; }

private string _foo;
public String Foo { get { return _foo; } set { _foo = value.Trim(); } }

その後、依存コードは引き続きパブリック プロパティのみを認識し、再コンパイルは必要ありません (クラスのインターフェイスが変更されていないため)。

ここで基本クラス ( Translation) がプロパティの動作を変更する場合、次のようにEnglishなります。

private string _english;
public String English { get { return _english; } set { _english = value.ToUpper(); } }

派生クラスでそれを取り上げたいと思うでしょう!

したがって、プロパティには動作が関連付けられていることを考慮して、その実装が派生クラスに望ましくない影響を与えない限り、常に親クラスの実装に委譲する必要があります。

于 2012-04-16T02:30:00.120 に答える
4

The first style is definitely preferable unless you have some good reason to pick the other one.

The automatically-implemented properties of Translation each add a field, and style B adds more rather than using the ones the compiler added. Style A reuses the one the compiler added, saving some storage.

Additionally, there's no need to override the superclass's properties if you're not going to change their functionality. You could even write another style like this:

public class TranslationStyleC : Translation {
    public TranslationStyleC(string e, string f) : base(e, f) {
    }
}
于 2012-04-16T02:26:23.880 に答える
3

スーパークラスの動作をまったく強化しないため、意図した効果を得るためにスーパークラスのプロパティをオーバーライドする必要はありません。

abstractbase から修飾子を削除するTranslationと、機能的に両方と同等になるため、サブクラスはもう必要ありません。

さて、いつ使用するかについてbase; サブクラスでオーバーライドされたスーパークラスの機能にアクセスする場合に使用する必要があります。呼び出しは、コンパイル時baseに常にスーパークラス メソッドに静的にバインドされます。スーパークラスのメソッドが(あなたの場合のように)であっても。通話で発生する可能性のある奇妙なことについては、こちらをご覧ください。virtualbase

于 2012-04-16T02:27:34.863 に答える
2

前述のように、スタイル Aはすでに宣言されているフィールドを再利用しますが、スタイル B は新しいフィールドを宣言しますbaseをいつ使用するかについての質問に関して、経験則は「親クラスで定義されたロジック/コードを再利用したいときはいつでも」です。

于 2012-04-16T02:35:34.637 に答える
1

それは、構造をどのように活用するつもりかということになります。

実装されると、TranslationStyleA のオーバーライドされたメンバーは少し冗長になります。コンシューマーは、基本派生でオーバーライドを提供しなくても基本メンバーに簡単にアクセスできるからです。このような場合、基本メンバーをオーバーライドしても設計に価値がなければ、個人的にはオーバーライドしません。

2 番目の実装は、基本クラス メンバーの設定とアクセスを本当にオーバーライドしたい場合に一般的です。たとえば、基本クラス メンバーの設定が別の操作を開始するための触媒である場合、派生のオーバーライドされたメンバーが適切な場所になります。それが起こるために。

于 2012-04-16T02:43:42.137 に答える