2015-12-27 14 views
9

Mit C# 6.0 ich tun kann, um diesenWie C# 6 null-bedingte in C# <6

var isEqual = x.Id == y.Id 
       && x.UpdatedAt == y.UpdatedAt 
       && x.Name == y.Name       
       && x.RulesUrl == y.RulesUrl 
       && x.OngoingChallenges?.Count == y.OngoingChallenges?.Count 
       && x.MembershipIds?.Count == y.MembershipIds?.Count; 

Gibt es eine schöne Lösung, dies zu tun mit C# < 6.0 zu emulieren?

Ich meine, das Teil

&& x.OngoingChallenges?.Count == y.OngoingChallenges?.Count 
&& x.MembershipIds?.Count == y.MembershipIds?.Count; 

Da in alten Projekten, die wir die Möglichkeit nicht haben C# 6.0 zu verwenden. Wie schreibe ich effizient?

+1

Nein, es ist nicht. – gdoron

+0

Was ist dann deine Lösung? – senzacionale

+3

@senzacionale, zum Beispiel ternärer Operator wie: '(x.MembershipIds == null? (Int?) Null: x.MembershipIds.Count)' – Grundy

Antwort

7

x.OnGoingChallenges?.Count entspricht x.OnGoingChallenges != null ? x.OnGoingChallenges.Count : default(int?) (es sind andere Ansätze, aber am Ende des Tages ist eine Verknüpfung auf null Prüfung null-bedingten Operator genannt) .

Das heißt, können Sie Ihren Code nicht mit einem syntaktisch elegante Erklärung ohne C# 6, neu geschrieben werden, aber Sie können diese neuen Methoden C# 6-Funktion Erweiterung emulieren ...

public static class StructExtensions 
{ 
    // Check that TProperty is nullable for the return value (this is how C#6's 
    // null-conditional operator works with value types 
    public static TProperty? GetOrDefault<TObject, TProperty>(this TObject someObject, Func<TObject, TProperty> propertySelectionFunc) 
     where TObject : class 
     where TProperty : struct 
    { 
     Contract.Requires(propertySelectionFunc != null); 

     return someObject == null ? default(TProperty?) : propertySelectionFunc(someObject); 
    } 
} 

Und jetzt Ihre Code in C# 5 würde wie folgt aussehen:

var isEqual = x.Id == y.Id 
          && x.UpdatedAt == y.UpdatedAt 
          && x.Name == y.Name       
          && x.RulesUrl == y.RulesUrl 
          && x.OngoingChallenges.GetOrDefault(c => c.Count) == y.OngoingChallenges.GetOrDefault(c => c.Count) 
          && x.MembershipIds.GetOrDefault(m => m.Count) == x.MembershipIds.GetOrDefault(m => m.Count); 

Die gesamte Erweiterungsmethode für die Zeugung eines Wert typisierte Eigenschaftswert oder den Standardwert funktionieren würde. Sie können die Erweiterungsmethodenklasse erweitern oder auch nicht, um auch das Abrufen eines Referenztypwerts oder null zu unterstützen.

+0

'someObject.Equals (default (TProperty))' sieht nicht richtig aus. Abgesehen davon, dass eine "NullReferenceException" ausgelöst wird, wenn "someObject" gleich "null" ist, sollten Sie mit "default (TObject)" und nicht mit "default (TProperty)" vergleichen. Und da du 'TObject: class' benötigst, weißt du' default (TObject) 'ist' null', an diesem Punkt kannst du einfach 'someObject == null' schreiben. – hvd

+0

Sehr gute Idee, aber ich habe einen Fehler: Enthält keine Definition für 'OngoingChallenges' ... – senzacionale

+0

@senzacionale Abgesehen von der 'GetOrDefault' Definition selbst falsch ist, hat die Beispielverwendung auch Probleme. Es sollte 'x.OngoingChallenges.GetOrDefault (c => c.Count)' sein, nicht 'x.OngoingChallenges.GetOrDefault (c => c.OnGoingChallenges.Count)'. Gleiches gilt für die anderen Anwendungen. :) Das sollte den Fehler erklären, den du siehst. – hvd

8

In C# Version < 6.0 Sie ternary expressions

var isEqual = x.Id == y.Id 
    && x.UpdatedAt == y.UpdatedAt 
    && x.Name == y.Name       
    && x.RulesUrl == y.RulesUrl 
    && (x.OngoingChallenges == null ? 0 : x.OngoingChallenges.Count) == 
     (y.OngoingChallenges == null ? 0 : y.OngoingChallenges.Count) 
    && (x.MembershipIds == null : 0 ? x.MembershipIds.Count) == 
     (y.MembershipIds == null : 0 : y.MembershipIds.Count); 

Wie @Hamlet Hakobyan hat darauf hingewiesen, verwenden würde, ist dies nicht die semantisch exakte Entsprechung der ursprünglichen C# 6.0-Lösung ?., aber man konnte es ändern (nach @hvd):

int? count = x.MembershipIds == null : default(int?) ? x.MembershipIds.Count; 

Es hängt davon ab, ob Sie eine fehlende Sammlung und eine leere Sammlung als gleich oder nicht zu betrachten.


Sie könnten auch die null-coalescing operator ?? verwenden und ein Ersatzobjekt zur Verfügung stellen. Unter der Annahme, dass Ihre Objekte sind Listen von irgendeiner Art:

var empty = new List<int>(); 
var isEqual = x.Id == y.Id 
    && x.UpdatedAt == y.UpdatedAt 
    && x.Name == y.Name       
    && x.RulesUrl == y.RulesUrl 
    && (x.OngoingChallenges ?? empty).Count == (y.OngoingChallenges ?? empty).Count 
    && (x.MembershipIds ?? empty).Count == (y.MembershipIds ?? empty).Count; 
+3

Ihre erste Lösung hat ein Problem. Es wird angenommen, dass "null gleich 0" ist, was eine falsche Annahme ist. –

+0

Das zweimalige Lesen der 'OngoingChallenges' -Eigenschaft kann bei der Ausführung einer neuen Liste (oder eines anderen Typs, der zurückgegeben wird) bei jedem Lesevorgang der Eigenschaft leistungsmäßig problematisch sein.Wenn es sich um eine schreibgeschützte Liste handelt, die jedoch auf eine Framework-Version abzielt, die nicht über 'ReadOnlyCollection' verfügt, kann dies ein guter Grund dafür sein, dass jedes Mal eine neue Liste zurückgegeben wird. Ihre zweite Option mit "leer" vermeidet dieses Problem. (Nit-Pick: Sie würden nicht auf "a == b" als "Sie würden einen binären Ausdruck verwenden", würden Sie den Gleichheitsoperator namentlich nennen. '?:' Heißt der bedingte Operator.) – hvd

+1

@ HamletHakobyan's Punkt ist auch ein fairer. Das kann leicht behoben werden, indem man '0' in 'default (int?)'/'(Int?) Null 'ändert. – hvd

2

Bevor C# 6, benutzte ich so etwas wie dieses

public static class CommonExtensions 
{ 
    public static TValue TryGet<TObject, TValue>(this TObject obj, Func<TObject, TValue> getter, TValue defaultValue = default(TValue)) 
     where TObject : class 
    { 
     return obj == null ? defaultValue : getter(obj); 
    } 

    //If objects types are equals 
    public static bool KeyEquals<TObject, TValue>(this TObject a, TObject b, Func<TObject, TValue> keyGetter) 
     where TObject : class 
    { 
     return a != null 
      && b != null 
      && EqualityComparer<TValue>.Default.Equals(keyGetter(a), keyGetter(b)); 
    } 
} 



var isEqual = x.Id == y.Id 
       && x.UpdatedAt == y.UpdatedAt 
       && x.Name == y.Name       
       && x.RulesUrl == y.RulesUrl 
       //v1 
       && x.OngoingChallenges.TryGet(v => v.Count) == y.OngoingChallenges.TryGet(v => v.Count) 
       //v2 
       && x.MembershipIds.KeyEquals(y.MembershipIds, v => v.Count);