たとえば、次のクラスがあるとします。
public class SomeClass
{
//...
private IEnumerable<SomeType> myEnumerable;
public IEnumerable<SomeType> MyEnumerable
{
get => myEnumerable;
set => myEnumerable = value ?? new List<SomeType>();
}
}
このタイプの強制をセッター(またはゲッター)で実行することは良い習慣ですか、またはプロパティがフィールドとは異なる動作をしないようにすることがより重要です-たとえば、次のシナリオでは:
var obj = new SomeClass();
IEnumerable<SomeType> enumerable = GetEnumerable(); //returns null
obj.MyEnumerable = enumerable;
Assert.AreEqual(enumerable, obj.MyEnumerable); //wrong!
ここにいくつかの組み合わせた質問があります。
はい、それが正当な場合。ただし、referenceタイプの場合は、単にそれを返すのではなく、実際にデフォルト値を格納する必要があります。そうしないと、予期しない動作が発生する可能性があります。
はい、それが正当な場合。
いいえ。この例では、まったく異なる問題の回避策としては不適切です。
なぜこの例でそれをすべきでないのか
コードの問題は、ソリューションが問題とは異なる場所に実装され、コードがばらばらになることです。
_public IEnumerable<SomeType> MyEnumerable
{
get => myEnumerable;
set => myEnumerable = value ?? new List<SomeType>();
}
_
ここでの「カスタム」ロジックは?? new List<SomeType>()
です。それ以外はすべて、プロパティのデフォルトの実装です。このコードは、「null
値をデフォルト値に変換する必要がある」と論理的に同等です。
null
値をデフォルト値に変換する必要がある場合。あなたが自問すべき最初の質問はnull
値がどこから来ているかです、これはここにあります:
_IEnumerable<SomeType> enumerable = GetEnumerable(); //returns null
_
そして今、ばらばらの問題/解決策を見る:
GetEnumerable()
メソッド内で発生します。GetEnumerable()
の戻り値を格納するために使用されるクラスのプロパティ内で発生します。これにはいくつかの理由で問題があります。
GetEnumerable()
が他の場所で使用されており、値をSomeOtherClass
に格納している場合、このクラスはも必要です潜在的なnull
値を処理します。これは、DRYの原則に違反しています。SomeClass
をGetEnumerable()
メソッドに不必要に関連付けます。これは、GetEnumerable()
メソッドの出力に(かなり)固有の検証が含まれているためです。これは、懸念の分離に違反します。最適なソリューション、優先順
GetEnumerable()
メソッドを変更します。
理想的には、メソッドがnull
を返さないようにする必要があります。これには例外がありますが、例には当てはまりません。
例えば。 SingleOrDefault()
のようなメソッドは、null
が意味のある戻り値であることを多少明示的に指定しています。 SingleOrDefault()
の代わりにSingle()
を使用することにより、開発者は、例外ではなくnull
(または定義済みのデフォルト)値を使用することを明示的に指定しました。したがって、開発者はawareであり、null
値に遭遇する可能性があります(別のデフォルト値を指定した場合を除く)。
これは単にGetEnumerable()
メソッド本体で修正されています。 returnステートメントが次の場合:
_return myVariable;
_
単に次のように変更できます。
_return myVariable ?? new List<SomeType>();
_
問題は解決しました。null
値は返されません。
ただし、メソッド自体にアクセスできない可能性があります。これが外部ライブラリからのメソッドである場合。
- 返された
null
をすぐに処理します。
簡単に言うと、メソッド呼び出しとnull処理をインライン化します。
_IEnumerable<SomeType> enumerable = GetEnumerable() ?? new List<SomeType>();
_
メソッドはまだnull
を返すことができますが、これは制御できませんが、すぐにnull
を空のリスト。以降のすべてのコードでは、null
値が渡されたかどうかを評価する必要がなくなりました。
サイドコメント:
このメソッドが多くの異なる場所で使用されている場合、null
値を処理する別のメソッドでメソッドをラップすることは価値があります。
_public List<SomeType> GetEnumerableOrDefault()
{
return GetEnumerable() ?? new List<SomeType>();
}
_
このように、?? new List<SomeType>()
を呼び出すたびにGetEnumerable()
を置く必要はありません。
ゲッター/セッターの値を変更したいその他の場合:
これは、値をいじりたい場合の使用例を改善するためのものです。
例えば.
_public int Age
{
get => _age;
set => _ age = value < 0 ? 0 : value;
}
_
注:現代の標準では、これはビジネスロジックをプロパティに入れると見なされます。これは眉をひそめています。ただし、このようなアプローチを使用するためにsimpleアプリケーションに対して引数を作成できます。
例えば.
_public string Name
{
get => _name ?? "John Doe";
set => _name = value;
}
_
注:
null
値を設定することを決定した人にあります。これは、事後にnull
値を受け取る不運な呼び出し元とは対照的です。get => String.IsNullOrWhitespace(_name) ? "John Doe" : _name;
。私にとって、開発者が書くのは混乱するようです
someObject.List = null;
someObject.Add(item);
この種の場合は例外をスローする方が理にかなっています。開発者は書くべきです
someObject.List = new List<SomeType>();
someObject.List.Add(item);
またはさらに良いことに、
someObject.List.Clear();
someObject.List.Add(item);
だから私はあなたのプロパティを実装するより標準的な方法は次のようになると思います:
public class SomeClass
{
private IEnumerable<SomeType> myEnumerable = new List<SomeType>();
public IEnumerable<SomeType> MyEnumerable
{
get => myEnumerable;
set => {
if (value == null) throw new ExceptionOfSomeKind();
myEnumerable = value;
}
}
}
またはこれ:
public class SomeClass
{
//This is readonly now-- can be set only when instantiated
private readonly List<SomeType> myEnumerable = new List<SomeType>();
public List<SomeType> MyList
{
get => myEnumerable;
//Notice no setter!!!
}
}
ゲッターとセッターでプロパティを使用する理由は、渡されたものを常に返す必要がないためです。
そうでなければ、なぜフィールド以外のものを使用するのですか?