クラスAの子オブジェクトのプロパティを参照するクラスAのプロパティを実装する方法


9

このコードは、簡略化すると次のようになります。

public class Room
{
    public Client Client { get; set; }

    public long ClientId
    {
        get
        {
            return Client == null ? 0 : Client.Id;
        }
    }
}

public class Client 
{
    public long Id { get; set; }
}

現在、3つの視点があります。

1)Clientプロパティは常に設定される必要があるため(つまりnullではない)、これは適切なコードでClient == nullあり、Id値0は偽のIDを示します(これはコードの作成者の意見です;-))

2)が0偽の値であることを呼び出し元に依存することはできません。Idまた、 Clientプロパティを常に設定する必要exceptionがあるget場合は、Clientプロパティがnullになったときにをスローする必要があります。

3)Clientプロパティを常に設定する必要がある場合は、プロパティがたまたまnullの場合に戻りClient.Id、コードにNullRef例外をスローさせClientます。

これらのうちどれが最も正しいですか?または、4つ目の可能性はありますか?


5
答えではなく単なるメモ:プロパティゲッターから例外をスローしないでください。
ブランドン

3
Brandonの要点について詳しく説明するには、stackoverflow.com
a / 1488488/569777

1
確かに:一般的な考え方は、(msdn.microsoft.com/en-us/library/...はstackoverflow.com/questions/1488472/...、他の人の間では)性質は、できるだけ行う軽量でかつクリーンを表すべきであるべきであるということです、オブジェクトの公開状態。インデクサーはわずかな例外です。デバッグ中にプロパティのウォッチウィンドウに例外が表示されるのも予期しない動作です。
ブランドン

1
プロパティゲッターをスローするコードを記述する必要はありません(必要ありません)。これは、オブジェクトがサポートされていない状態になったために発生する例外を非表示にして、飲み込む必要があるという意味ではありません。
ベン

4
Room.Client.Idを実行できないのはなぜですか?なぜそれをRoom.ClientIdにラップするのですか?クライアントをチェックする場合は、Room.HasClient {get {return client == null; }}これはもっと簡単ですが、実際にIDが必要なときに、通常のRoom.Client.Idを実行できますか?
ジェームズ

回答:


25

Roomクラスの状態の数を制限するようなにおいがします。

Clientnullのときに何をすべきかについて質問しているという事実自体が、Room状態空間が大きすぎるというヒントです。

物事をシンプルに保つためClientに、Roomインスタンスのプロパティがnull になることは許可しません。つまり、内部のコードはがnullになることはないRoomと想定しても安全Clientです。

将来的には何らかの理由場合Clientになりnull、その状態をサポートする衝動に抵抗します。そうすることで、メンテナンスが複雑になります。

代わりに、コードが失敗してすぐに失敗するようにします。結局のところ、これはサポートされている状態ではありません。アプリケーションがこの状態になると、あなたはもうノーリターンの線を越えました。次に行う唯一の合理的なことは、アプリケーションを閉じることです。

これは、未処理のnull参照例外の結果として(自然に)発生する可能性があります。


2
いいね。私の考えのように「私は今までにもnullに任意のルームインスタンスのクライアントのプロパティを許可しないだろう、物事をシンプルに保つために。」それがnullのときに何をすべきか、実際より良い質問へのより
ミシェル・

@Michel後でその要件を変更する場合は、null値をNullObject(またはNullClient)で置き換えることができます。これにより、既存のコードで引き続き機能し、必要に応じて存在しないクライアントの動作を定義できます。問題は、「クライアントなし」のケースがビジネスロジックの一部であるかどうかです。
null

3
まったく正しい。サポートされていない状態をサポートするためにコードの1文字を記述しないでください。NullRefをスローさせるだけです。
ベン

@ベン同意しない; MSのガイドラインでは、プロパティゲッターは例外をスローしてはならないことを明示しています。そして、より意味のある例外がスローされる可能性があるときにnull参照がスローされることを許可するのは、単に怠惰です。
アンディ

1
@Andyがガイドラインの理由を理解すると、いつ適用されないかを知ることができます。
ベン

21

いくつかの考慮事項:

a)Clientインスタンス自体のパブリックゲッターがすでにあるのに、ClientId専用のゲッターがあるのはなぜですか?ClientIdがaであるという情報longがRoomのシグネチャに石に刻まれなければならない理由がわかりません。

b)セカンドオピニオンに関して、定数を導入できますInvalid_Client_Id

c)意見1と3(および私の主要なポイント)について:部屋には常にクライアントが必要ですか?多分それは単なる意味論ですが、これは正しく聞こえません。おそらく、RoomとClientを完全に分離したクラスと、それらを結び付ける別のクラスを用意する方が適切でしょう。たぶん、予約、予約、占有?(これは、実際に何をしているのかによって異なります。)そして、そのクラスでは、「部屋が必要」と「クライアントが必要」という制約を適用できます。


5
「クライアントIDが長いという情報をルームのシグネチャに石に刻む必要がある理由はわかりません」
Michel

あなたの英語は大丈夫です。;)この答えを読んだだけでは、あなたがそう言わなければ、あなたの母国語が英語でないことは知りませんでした。
jpmc26 2015

ポイントBとCは良いです。RE:a、その便利な方法、そして部屋がクライアントへの参照を持っているという事実は、実装の詳細にすぎないかもしれません(クライアントは公に見えるべきではないと主張できます)
Andy

17

3つの意見すべてに同意しません。Client決してできない場合はnullそれを可能にすることさえしないでくださいnull

  1. 値を設定しClient、コンストラクタに
  2. ArgumentNullExceptionコンストラクタにをスローします。

したがって、コードは次のようになります。

public class Room
{
    private Client theClient;

    public Room(Client client) {
        if(client == null) throw new ArgumentNullException();
        this.theClient = client;
    }

    public Client Client { 
        get { return theClient; }
        set 
        {
            if (value == null) 
                throw new ArgumentNullException();
            theClient = value;
        }
    }

    public long ClientId
    {
        get
        {
            return theClient.Id;
        }
    }
}
public class Client 
{
    public long Id { get; set; }
}

これはコードとは関係ありませんが、の不変バージョンを使用RoomしてtheClient readonly、クライアントが変更された場合は新しい部屋を作成することをお勧めします。これにより、私の回答の他の側面のnullセーフティに加えて、コードのスレッドセーフティが向上します。ミュータブルvsイミュータブルに関するこのディスカッションをご覧ください


1
これらはArgumentNullExceptionsであるべきではありませんか?
Mathieu Guindon

4
いいえ。プログラムコードはNullReferenceException.Net VMによって「nullオブジェクト参照を逆参照しようとした場合」にスローされることはありません。「null参照(Visual BasicではNothing)が有効な引数として受け入れないメソッドに渡された場合」にArgumentNullExceptionスローされるをスローする必要があります。
Pharap

2
@Pharap私はC#コードを記述しようとしているJavaプログラマーです。そのような間違いを犯すと思いました。
durron597 2015

@ durron597「final」という用語の使用から、このことを推測していたはずです(この場合、対応するC#キーワードはですreadonly)。私は編集したばかりでしたが、コメントが理由をよりよく説明するのに役立つと感じました(将来の読者のために)。もしあなたがこの答えをまだ与えていなかったら、私はまったく同じ解決策を与えるでしょう。不変性も良い考えですが、期待するほど簡単ではありません。
Pharap

2
プロパティは通常、例外をスローすべきではありません。ArgumentNullExceptionをスローするセッターの代わりに、代わりにSetClientメソッドを提供します。誰かがこれに関する回答へのリンクを質問に投稿しました。
アンディ

5

2番目と3番目のオプションは回避する必要があります。ゲッターは、制御できない例外を除いて、呼び出し元を叩くべきではありません。

Clientをnullにできるかどうかを決定する必要があります。その場合、呼び出し元がアクセスする前にそれがnullかどうかを確認する方法を提供する必要があります(たとえば、bool ClientIsNullプロパティ)。

Clientをnullにできないと判断した場合は、それをコンストラクタの必須パラメータにして、nullが渡された場合に例外をスローします。

最後に、最初のオプションにはコードのにおいも含まれます。クライアントに独自のIDプロパティを処理させる必要があります。含まれているクラスでゲッターを呼び出すだけのコンテナークラスでゲッターをコーディングするのはやり過ぎのようです。クライアントをプロパティとして公開するだけです(それ以外の場合は、クライアントがすでに提供しているすべてのものを複製することになります)。

long clientId = room.Client.Id;

Clientをnullにできる場合は、少なくとも呼び出し元に責任を与えます。

if (room.Client != null){
    long clientId = room.Client.Id;
    /* other code follows... */
}

1
「クライアントがすでに提供しているすべてのものを複製することになる」と私は考えています
Pharap 2015

2

nullのClientプロパティがサポートされている状態である場合は、NullObjectの使用を検討してください

しかし、これは例外的な状態である可能性が高いので、nullになることを不可能(またはあまり便利ではない)にする必要がありますClient

public Client Client { get; private set; }

public Room(Client client)
{
    Client = client;
}

public void SetClient(Client client)
{
    if (client == null) throw new ArgumentNullException();
    Client = client;
}

ただし、それがサポートされていない場合は、焼き半分のソリューションで時間を無駄にしないでください。この場合:

return Client == null ? 0 : Client.Id;

ここでNullReferenceExceptionを「解決」しましたが、多大なコストがかかりました。これにより、のすべての呼び出し元がRoom.ClientId0をチェックするように強制されます。

var aRoom = new Room(aClient);
var clientId = aRoom.ClientId;
if (clientId == 0) RestartRoombookingWizard();
else ContinueRoombooking(clientid);

奇妙なバグは、ある時点で "ErrorCode"の戻り値をチェックするのを忘れて他の開発者(またはあなた自身!)と協力する可能性があります。
安全にプレイしてください。NullReferenceExceptionがスローされ、呼び出し元がさらに混乱するのではなく、呼び出しスタックの上位のどこかに「優雅に」キャッチできるようにします...

別の見方をすれば、TellDontAskを公​​開したいClientClientId考えていて、TellDontAskについても考えている場合です。達成したいことを伝えるのではなく、オブジェクトに要求しすぎると、すべての結合が終了し、後で変更が難しくなる場合があります。


これNotSupportedExceptionは誤用されていますArgumentNullException。代わりに使用する必要があります。
Pharap

1

現在リリースされているc#6.0以降は、これを実行するだけです。

public class Room
{
    public Room(Client client)
    {
        this.Client = client;
    }

    public Client Client { get; }
}

public class Client
{
    public Client(long id)
    {
        this.Id = id;
    }

    public long Id { get; }
}

Clienttoのプロパティを上げることRoomは、カプセル化とDRY原則の明らかな違反です。

あなたがアクセスする必要がある場合IdClientRoomあなたが行うことができますが、

var clientId = room.Client?.Id;

使用に注意してくださいヌル条件演算子をclientIdとなりますlong?場合、ClientあるnullclientIdとなりnullそうでない場合は、clientIdの値を持つことになりますClient.Id


しかし、型clientidはnull可能ですlong?
ミシェル

@Michelよく、部屋にクライアントが必要な場合は、トラクターにnullチェックを入れます。その後var clientId = room.Client.Id、両方とも安全になりlongます。
Jodrell 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.