C#では、この例ではPropertyCから値を取得し、ObjectA、PropertyA、およびPropertyBはすべてnullにできるとしましょう。
ObjectA.PropertyA.PropertyB.PropertyC
最小限のコードでPropertyCを安全に入手するにはどうすればよいですか?
今、私はチェックします:
if(ObjectA != null && ObjectA.PropertyA !=null && ObjectA.PropertyA.PropertyB != null)
{
// safely pull off the value
int value = objectA.PropertyA.PropertyB.PropertyC;
}
このようなこと(疑似コード)を行うと便利です。
int value = ObjectA.PropertyA.PropertyB ? ObjectA.PropertyA.PropertyB : defaultVal;
おそらく、ヌル合体演算子でさらに崩壊しました。
[〜#〜] edit [〜#〜]もともと私の2番目の例はjsに似ていると言っていましたが、jsでは機能しないことが正しく指摘されていたため、それをpsuedo-codeに変更しました。
C#6では、 Null Conditional Operator を使用できます。したがって、元のテストは次のようになります。
int? value = objectA?.PropertyA?.PropertyB?.PropertyC;
短い拡張方法:
public static TResult IfNotNull<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator)
where TResult : class where TInput : class
{
if (o == null) return null;
return evaluator(o);
}
を使用して
PropertyC value = ObjectA.IfNotNull(x => x.PropertyA).IfNotNull(x => x.PropertyB).IfNotNull(x => x.PropertyC);
この単純な拡張メソッドと、はるかに多くを見つけることができます http://devtalk.net/csharp/chained-null-checks-and-the-maybe-monad/
編集:
しばらく使用した後、このメソッドの適切な名前は、元のWith()ではなくIfNotNull()であると思います。
クラスにメソッドを追加できますか?そうでない場合、拡張メソッドを使用することを考えましたか? GetPropC()
というオブジェクトタイプの拡張メソッドを作成できます。
例:
public static class MyExtensions
{
public static int GetPropC(this MyObjectType obj, int defaltValue)
{
if (obj != null && obj.PropertyA != null & obj.PropertyA.PropertyB != null)
return obj.PropertyA.PropertyB.PropertyC;
return defaltValue;
}
}
使用法:
int val = ObjectA.GetPropC(0); // will return PropC value, or 0 (defaltValue)
ところで、これは.NET 3以降を使用していることを前提としています。
あなたのやり方は正しいです。
あなたcouldは、Linq式を使用して here のようなトリックを使用します:
int value = ObjectA.NullSafeEval(x => x.PropertyA.PropertyB.PropertyC, 0);
しかし、各プロパティを手動でチェックするのははるかに遅いです...
デメテルの法則 を観察するためのリファクタリング
更新2014:C#6には新しい演算子?.
「安全なナビゲーション」または「ヌル伝播」と呼ばれるさまざまな
parent?.child
これは長い間非常に人気のあるリクエストでした https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/3990187-add-operator-to-c-?tracking_code=594c10a522f8e9bc987ee4a5e2c0b38d
あなたは明らかにNullable Monadを探しています:
string result = new A().PropertyB.PropertyC.Value;
になる
string result = from a in new A()
from b in a.PropertyB
from c in b.PropertyC
select c.Value;
これは、null許容プロパティのいずれかがnullの場合、null
を返します。それ以外の場合、Value
の値。
class A { public B PropertyB { get; set; } }
class B { public C PropertyC { get; set; } }
class C { public string Value { get; set; } }
LINQ拡張メソッド:
public static class NullableExtensions
{
public static TResult SelectMany<TOuter, TInner, TResult>(
this TOuter source,
Func<TOuter, TInner> innerSelector,
Func<TOuter, TInner, TResult> resultSelector)
where TOuter : class
where TInner : class
where TResult : class
{
if (source == null) return null;
TInner inner = innerSelector(source);
if (inner == null) return null;
return resultSelector(source, inner);
}
}
このコードは「最小限のコード」ですが、ベストプラクティスではありません。
try
{
return ObjectA.PropertyA.PropertyB.PropertyC;
}
catch(NullReferenceException)
{
return null;
}
型の値が空であると仮定すると、1つのアプローチは次のようになります。
var x = (((objectA ?? A.Empty).PropertyOfB ?? B.Empty).PropertyOfC ?? C.Empty).PropertyOfString;
私はC#の大ファンですが、new Java(1.7?)の非常に素晴らしいことは。?演算子です:
var x = objectA.?PropertyOfB.?PropertyOfC.?PropertyOfString;
新しいC#6.0で何かを見ました。これは「?」を使用することによるものですチェックする代わりに
たとえば、代わりに
if (Person != null && Person.Contact!=null && Person.Contact.Address!= null && Person.Contact.Address.City != null)
{
var city = person.contact.address.city;
}
あなたは単に使う
var city = person?.contact?.address?.city;
それが誰かの助けになることを願っています。
更新:
あなたは今このようにすることができます
var city = (Person != null)?
((Person.Contact!=null)?
((Person.Contact.Address!= null)?
((Person.Contact.Address.City!=null)?
Person.Contact.Address.City : null )
:null)
:null)
: null;
そのような呼び出しを連鎖する必要があるとき、私は作成したヘルパーメソッドTryGet()に依存します。
public static U TryGet<T, U>(this T obj, Func<T, U> func)
{
return obj.TryGet(func, default(U));
}
public static U TryGet<T, U>(this T obj, Func<T, U> func, U whenNull)
{
return obj == null ? whenNull : func(obj);
}
あなたの場合、次のように使用します:
int value = ObjectA
.TryGet(p => p.PropertyA)
.TryGet(p => p.PropertyB)
.TryGet(p => p.PropertyC, defaultVal);
これを行うことができます:
class ObjectAType
{
public int PropertyC
{
get
{
if (PropertyA == null)
return 0;
if (PropertyA.PropertyB == null)
return 0;
return PropertyA.PropertyB.PropertyC;
}
}
}
if (ObjectA != null)
{
int value = ObjectA.PropertyC;
...
}
または、さらに良いかもしれません:
private static int GetPropertyC(ObjectAType objectA)
{
if (objectA == null)
return 0;
if (objectA.PropertyA == null)
return 0;
if (objectA.PropertyA.PropertyB == null)
return 0;
return objectA.PropertyA.PropertyB.PropertyC;
}
int value = GetPropertyC(ObjectA);
それは不可能。 ObjectA.PropertyA.PropertyBは、ObjectAがnull参照解除のためにnullの場合に失敗しますが、これはエラーです。
if(ObjectA!= null && ObjectA.PropertyA ...は短絡のために機能します。つまり、ObjectAがnullの場合、ObjectA.PropertyAはチェックされません。
最初に提案する方法は、意図を持って最も明確かつ明確にすることです。どちらかといえば、非常に多くのヌルに依存することなく、再設計を試みることができます。
次の拡張子を使用できますが、本当に良いと思います:
/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<TF, TR>(TF t, Func<TF, TR> f)
where TF : class
{
return t != null ? f(t) : default(TR);
}
/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<T1, T2, TR>(T1 p1, Func<T1, T2> p2, Func<T2, TR> p3)
where T1 : class
where T2 : class
{
return Get(Get(p1, p2), p3);
}
/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<T1, T2, T3, TR>(T1 p1, Func<T1, T2> p2, Func<T2, T3> p3, Func<T3, TR> p4)
where T1 : class
where T2 : class
where T3 : class
{
return Get(Get(Get(p1, p2), p3), p4);
}
そして、次のように使用されます。
int value = Nulify.Get(objectA, x=>x.PropertyA, x=>x.PropertyB, x=>x.PropertyC);
この投稿を偶然見つけました。
しばらく前に、Visual Studio Connectで新しい???
演算子。
これにはフレームワークチームからのいくらかの作業が必要になりますが、言語を変更する必要はなく、コンパイラの魔法をかけるだけです。コンパイラはこのコードを変更する必要があるという考えでした(構文はatmを許可されていません)
string product_name = Order.OrderDetails[0].Product.Name ??? "no product defined";
このコードに
Func<string> _get_default = () => "no product defined";
string product_name = Order == null
? _get_default.Invoke()
: Order.OrderDetails[0] == null
? _get_default.Invoke()
: Order.OrderDetails[0].Product == null
? _get_default.Invoke()
: Order.OrderDetails[0].Product.Name ?? _get_default.Invoke()
Nullチェックの場合、これは次のようになります。
bool isNull = (Order.OrderDetails[0].Product ??? null) == null;
Nullable型と同様のパターンを使用して、PropertyAの型で独自のメソッド(または、型でない場合は拡張メソッド)を記述します。
class PropertyAType
{
public PropertyBType PropertyB {get; set; }
public PropertyBType GetPropertyBOrDefault()
{
return PropertyB != null ? PropertyB : defaultValue;
}
}
このアプローチは、ラムダゴブリーゴークを乗り越えると、かなり簡単です。
public static TProperty GetPropertyOrDefault<TObject, TProperty>(this TObject model, Func<TObject, TProperty> valueFunc)
where TObject : class
{
try
{
return valueFunc.Invoke(model);
}
catch (NullReferenceException nex)
{
return default(TProperty);
}
}
次のような使用法があります。
ObjectA objectA = null;
Assert.AreEqual(0,objectA.GetPropertyOrDefault(prop=>prop.ObjectB.ObjectB.ObjectC.ID));
Assert.IsNull(objectA.GetPropertyOrDefault(prop => prop.ObjectB));
デフォルト値を受け入れるメソッドを作成しました。使用方法は次のとおりです。
var teacher = new Teacher();
return teacher.GetProperty(t => t.Name);
return teacher.GetProperty(t => t.Name, "Default name");
コードは次のとおりです。
public static class Helper
{
/// <summary>
/// Gets a property if the object is not null.
/// var teacher = new Teacher();
/// return teacher.GetProperty(t => t.Name);
/// return teacher.GetProperty(t => t.Name, "Default name");
/// </summary>
public static TSecond GetProperty<TFirst, TSecond>(this TFirst item1,
Func<TFirst, TSecond> getItem2, TSecond defaultValue = default(TSecond))
{
if (item1 == null)
{
return defaultValue;
}
return getItem2(item1);
}
}