2015-09-29 41 views
5

私は、動作するとは思っていない、実際に動作する、奇妙なものを発見しました。私は、コンストラクタ基底クラスから(一定の)プロパティサブクラスにアクセスすることができる:サブクラス 'プロパティから基本クラス'コンストラクタにアクセス

public abstract class Parent { 

    public Parent() { 
    var constName = ConstName;   // <-- surprisingly, this works 
    var randomName = RandomName;   // <-- surprisingly, this works 
    } 

    public abstract string ConstName { get; } 

    public abstract string RandomName { get; } 

} 


public class Child : Parent { 

    public override string ConstName { get { return "Mike"; } } 

    public override string RandomName { get { return new Random().Next().ToString(); } } 

} 

Name非静的なくフィールドです。私はいつもタイプのイニシャライザ(staticconstフィールド)が実行され、次にその基本クラスのもの、次にベースのctor、次にサブクラスのctorが実行されたと考えました。 これは、子プロセスが親プロセス中に完全に構​​築されていないことを意味します。

は、すべての状況下で動作します。この「法的な」C#のですか?なぜこれは機能しますか?

編集:

いいえ、デュープの質問ではありません。私の質問にはクラススキーマはありません。

+0

親の中にNameプロパティがあります。技術的にはこれは正当なものです。 –

+0

@Nikitaはい、しかし子供はまだ建設されていません。私の質問で初期化の順序を読んでください。 –

+0

だから、あなたがデバッグしてベース建設に行くと、あなたはマイクとして名前がついていると言っていますか? –

答えて

3

あなたは、サブタイプの抽象/仮想メンバーにアクセスすることができます。プロパティgetterは最後のメソッドです。

しかし。あなたが言ったように

、サブクラスのコンストラクタは、まだ実行されていませんでした。したがって、副作用がある可能性があります。だからあなたはFxCopから警告を受ける。

public abstract class Parent 
{ 
    public Parent() 
    { 
    // NullReferenceException here: 
    var nameLength = Name.Length; 
    } 

    public abstract string Name { get; } 

} 

public class Child : Parent 
{ 
    private string name; 

    public Child() 
    { 
    name = "My Name"; 
    } 

    public override string Name { get { return name; } } 
} 

さらに悪い:

public class Child : Parent 
{ 
    private string name; 

    public Child() 
    { 
    name = "My Name"; 
    } 

    public override string Name 
    { 
    get 
    { 
     // NullReferenceException here. 
     // You didn't expect that this code is executed before 
     // the constructor was, did you? 
     return name.Substring(0, name.Length - 1); 
    } 
    } 
} 

ので、それが行うことは推奨されません。

+0

だから私はこの行動に驚かされるのは間違っていたので正しかった。コンパイラチームはそれを許してくれましたが、FxCopの警告を作成して、「非常に悪い考えです」と伝えました。基本的に、これは非決定論的な出力になります! –

+0

華麗な編集!問題を完全に説明しています! +1 –

+0

私の質問はこのように考えることができると思います。何かがコンパイルされて「正しい」ことができないからです。コンパイラが教えてくれない奇妙なエッジケースがあります。コンストラクタには暗い魔法がたくさんあります:)。 –

4

親が、抽象プロパティParent.Nameを持っています。抽象的であるため、Parentの(サブクラス)インスタンスがプロパティNameを実装することを約束します。もしそうでなければ、そのオブジェクトを作成することはできません。インスタンスを作成することができません。私が言う

注意。サブクラスがプロパティNameを実装していない場合、クラスは存在する可能性がありますが、インスタンス化することはできません。

クラスParentはプロパティNameを実装していないため、Parentをインスタンス化できません。

クラスChildは名前を実装するため、インスタンス化することができます。また、クラスParentのすべてのオブジェクト(=インスタンス化)にNameプロパティがあると約束したので、あなたが知っていることはすべてParent 、あなたはまたそれが名前を持っていることを知っています。

これは Wikipedia about polymorphism

をサブタイピングにそれはあなたがサブクラス化したい主な理由であるかもしれない多型の基本原理です。

+0

はい、意味があります。コンパイル時の契約を指しているので、子プロパティの存在を確認することができます。しかし、ここのポイントは、子供が(完全に)建設される前にそれをやっているということです。それは私が理解できないものですか? –

+0

2回目の読解では、あなたの答えは基本的に多型の全体的な考え方を(非常に)説明します。しかし、この質問の中心にある建設の順序ではありません。 –

+1

抽象関数のため、Parentのコンストラクタは、構築されるオブジェクトがParentのサブクラスであることを認識します。サブクラスの構築はまだ完了していませんが、サブクラスはすでに完全に存在しています。どのコンストラクタでも、基本クラスがなくても、オブジェクトの項目を初期化する順序に注意する必要があります。メソッドとプロパティを呼び出すことができますが、すでに初期化されている項目だけを使用するようにしてください。 –

関連する問題