2013-01-26 6 views
8

Ich habe 2 Sammlungen von 2 verschiedenen Typen, aber habe fast den gleichen Satz von Feldern. In einer Funktion muss ich eine der Sammlungen durchlaufen, abhängig von einer Bedingung. Ich möchte nur einen Codeblock schreiben, der beide Fälle abdeckt. Beispiel: Ich habe den folgenden Code:Wie verwende ich den gleichen Foreach Code für 2 Kollektionen?

if (condition1) 
{ 
    foreach(var type1var in Type1Collection) 
    { 

    // Do some code here 
     type1var.Notes = "note"; 
     type1var.Price = 1; 
    } 
} 
else 
{ 
    foreach(var type2var in Type2Collection) 
    { 

    // the same code logic is used here 
     type2var.Notes = "note";   
     type2var.Price = 1; 
    } 
} 

Jetzt: Ich habe diesen Code vereinfachen wollen nur einmal die gleiche Logik zu verwenden (wie sie identisch sind), so etwas wie die folgenden (PS: Ich weiß, das folgende Code nicht richtig ist, ich bin nur zu erklären, was ich tun möchte):

var typecollection = Condition1 ? Type1Collection : Type2Collection; 

foreach(var typevar in TypeCollection) 
{ 

    // the same code logic is used here 
    typevar.Notes = "note"; 
    typevar.Price = 1;  
} 

die Definition von Typ1 & Typ2 wie dem folgenden Code ähnlich ist (Eigentlich sind sie Objekte Entity):

public class Type1 : EntityObject 
    { 
     public int Type1ID { get; set; } 
     public int Type1MasterID { get; set; } 

     public String Notes { get; set; } 
     public decimal Price { get; set; } 
    } 

    public class Type2 : EntityObject 
    { 
     public int Type2ID { get; set; } 
     public int Type2MasterID { get; set; } 

     public String Notes { get; set; } 
     public decimal Price { get; set; } 
    } 

Update 1:

ich einige Beispiel-Code enthalten sind, die ich in foreach Block bin mit (ich eine öffentliche Eigenschaften der 2 Typen am Zugriff).

Update 2:

I Probe Typ1 & Typ2 Definitionen enthalten sind, wie Sie Ich habe 2 gemeinsame öffentliche Eigenschaften in beiden Klassen sehen, die ich in foreach-Block aktualisiert werden soll.

Update 3:

ich für die Verwirrung bedaure, Typ1 & Typ2 aus EntityObject abgeleitet (Sie sind beide Teil meines Entitätsmodell und die Type1Collection & Type2Collection sind tatsächlich EntityCollection dieser 2 Einheiten

.
+0

können Sie zwei Beispieltypdefinitionen in Ques einschließen – Simon

Antwort

2

Wenn Jon Skeet die Methode Concat von LINQ verwendet und die OP-Anweisung, dass die beteiligten Klassen EntityObject sind, ist hier eine andere mögliche Lösung. Dies setzt voraus, dass die EntityObject Subklassen als Teil Klassen definiert sind:

public partial class Type1 : EntityObject 
{ 
    public int Type1ID { get; set; } 
    public int Type1MasterID { get; set; } 
    public String Notes { get; set; } 
    public decimal Price { get; set; } 
} 

public partial class Type2 : EntityObject 
{ 
    public int Type2ID { get; set; } 
    public int Type2MasterID { get; set; } 

    public String Notes { get; set; } 
    public decimal Price { get; set; } 
} 

Dies ist die OP ermöglicht eine Schnittstelle mit den gemeinsamen Eigenschaften zu erklären, und hat seinen EntityObject Subklassen implementieren diese Schnittstelle:

public interface IMyType 
{ 
    String Notes { get; set; } 
    decimal Price { get; set; } 
} 
public partial class Type1 : IMyType {} 
public partial class Type2 : IMyType {} 

Und der ursprüngliche Code wird:

var query = (
    from type1var in type1Collection 
    where condition1 
    select (IMyType)type1var 
    ).Concat(
    from type2var in type2Collection 
    where !condition1 
    select (IMyType)type2var 
    ); 
foreach(var myType in query) 
{ 
    myType.Notes = "note"; 
    myType.Price = 1; 
} 
9

könnten Sie dynamisch verwenden. Hinweis werden Sie Typsicherheit verlieren.

var list1 = new List<bool>(){true,false}; 
var list2 = new List<int>(){1,2}; 

var typecollection = condition1 ? list1.Cast<dynamic>() : list2.Cast<dynamic>(); 
foreach (var value in typecollection) 
{ 
    //then you can call a method you know they both have 
    Debug.WriteLine(value.ToString()); 
} 

Oder wenn sie eine gemeinsame Schnittstelle teilen, können Sie direkt auf das werfen. Sie werden maint Ain Typ Sicherheit

var list1 = new List<bool>(){true,false}; 
var list2 = new List<int>(){1,2}; 

var typecollection = condition1 ? list1.Cast<IConvertible>() : list2.Cast<IConvertible>(); 
foreach (IConvertible convertible in typecollection) 
{ 
    //we now know they have a common interface so we can call a common method 
    Debug.WriteLine(convertible.ToString()); 
} 
+0

Obwohl ich Ihre Lösung interessant finde, bevorzuge ich immer noch die Sicherheit bei der Kompilierungszeit. Anstatt dynamische Programmierung für Ihr Beispiel zu verwenden (wo ToString eine Methode aus der Objektklasse ist), könnten Sie Kovarianz verwenden (mit einer Variablen vom Typ IEnumerable ) –

+1

@DanielCastro meine zweite Lösung hat Kompilierzeit Sicherheit – Simon

+0

Noch würde vorschlagen, IEnumerable anstatt die Liste direkt zu verwenden, wenn der einzige Zweck zu lesen ist. Wenn Sie eine Liste verwenden, können Sie versehentlich ein inkompatibles Element hinzufügen, und Sie werden es wahrscheinlich nur bis zur Laufzeit bemerken. –

0

Sie können es mit Prädikat und Aktion in einem Wörterbuch gespeichert. Ich schlage vor, hier Aktion, da der Code-Schnipsel nichts

public class IterationExample 
{ 
    private readonly Dictionary<bool, Action> dictionary; 

    public IterationExample() 
    { 
     dictionary = new Dictionary<bool, Action> { { true, CollectionOneIterator }, { false, CollectionTwoIterator } }; 
    } 

    public void PublicMethod() 
    { 
     dictionary[condition](); 
    } 

    private void CollectionOneIterator() 
    { 
     foreach (var loopVariable in Type1Collection) 
     { 
      //Your code here 
     } 
    } 

    private void CollectionTwoIterator() 
    { 
     foreach (var loopVariable in Type2Collection) 
     { 
      //Your code here 
     } 

    } 
} 

die readbility Mit dieser Art und Weise zurück nicht scheint und Testbarkeit des Codes verbessert und vermeidet auch lange Methoden.

Edit:

public class Entity 
{ 
    public IList<string> Type1Collection { get; set; } 
    public IList<string> Type2Collection { get; set; } 
} 

public class ConsumingClass 
{ 
    public void Example() 
    { 
     var entity = new Entity(); 
     entity.PublicMethod(); 
    } 
} 

public static class IterationExample 
{ 
    private static readonly Dictionary<bool, Action<Entity>> dictionary; 

    static IterationExample() 
    { 
     dictionary = new Dictionary<bool, Action<Entity>> { { true, CollectionOneIterator }, { false, CollectionTwoIterator } }; 
    } 

    public static void PublicMethod(this Entity entity) 
    { 
     dictionary[condition](); 
    } 

    private static void CollectionOneIterator(Entity entity) 
    { 
     foreach (var loopVariable in entity.Type1Collection) 
     { 
      //Your code here 
     } 
    } 

    private static void CollectionTwoIterator(Entity entity) 
    { 
     foreach (var loopVariable in entity.Type2Collection) 
     { 
      //Your code here 
     } 
    } 
} 
+0

Die Typen, die ich benutze, sind Entity-Objekte. Ich glaube, ich kann die Klassen nicht ändern, wie du es erwähnst. –

+0

@ AdelKhayata können Sie Erweiterung Methode verwenden, um dies zu überwinden, finden Sie in meiner aktualisierten Antwort –

+0

Mit dem gleichen Code in zwei separaten Methoden ('CollectionOneIterator' und' CollectionTwoIterator') nicht das ursprüngliche Problem der gleichen Code an zwei Orten dupliziert . –

0

Sie könnten einen Basistyp für Typ1 erstellen und Typ2, die die gemeinsamen Eigenschaften zwischen den beiden Klassen Gruppen:

class MyBaseType { 
    // Common properties 
} 

class Type1 : MyBaseType { 
    // Specific properties 
} 

class Type2 : MyBaseType { 
    // Specific properties 
} 

Dann Sie so etwas tun könnte:

IEnumerable<MyBaseType> collection; 
if(condition1) 
    collection = type1Collection; 
else 
    collection = type2Collection; 

foreach(MyBaseType element in collection) { 
    // Common logic 
} 

EDIT: Wie Simon in den Kommentaren darauf hinweist, sollten Sie verwenden eine Schnittstelle anstelle eines Basistyps, wenn es genug ist (i.Sie benötigen für beide Typen keine spezifische Implementierung.

+4

Bitte verwenden Sie keinen Basistyp, wenn eine Schnittstelle – Simon

+0

@Simon Richtig ist, nur wir sind nicht sicher, ob eine Basisklasse für diesen Fall gut ist, weil wir nicht genügend Informationen haben. Aber, wieder, Sie haben Recht: Wenn eine Schnittstelle ist genug, das ist der Weg zu gehen –

0

Dies ist keine sehr nette Art, es zu tun, aber es würde zumindest funktionieren.

 var type1Collection = new Collection<Type1>(); 
     var type2Collection = new Collection<Type2>(); 

     var condition1 = new Random().Next(0, 2) != 0; 

     dynamic selectedCollection; 
     if (condition1) 
      selectedCollection = type1Collection; 
     else 
      selectedCollection = type2Collection; 

     foreach (var typeVar in selectedCollection) 
     { 
      typeVar.Notes = "note"; 
      typeVar.Price = 1; 
     } 
+0

Wenn ich Ihre Lösung versuchen, bekomme ich den folgenden Fehler: Fehler Foreach-Anweisung kann nicht auf Variablen des Typs 'System.Collections. IEnumerable 'weil' System.Collections.IEnumerable 'keine öffentliche Definition für' GetEnumerator 'enthält –

0

Ich bin überrascht, niemand hat sonst eine Erweiterungsmethode noch vorgeschlagen:

public interface IMyType 
{ 
    String Notes { get; set; } 
    decimal Price { get; set; } 
} 

public static class MyTypeExtensions 
{ 
    public static void MyLogic(this IMyType myType) 
    { 
     // whatever other logic is needed 
     myType.Notes = "notes"; 
     myType.Price = 1; 
    } 
} 

jetzt Ihre ursprüngliche Typen müssen nur IMyType implementieren:

public class Type1 : IMyType 
{ 
    public int Type1ID { get; set; } 
    public int Type1MasterID { get; set; } 

    public String Notes { get; set; } 
    public decimal Price { get; set; } 
} 

public class Type2 : IMyType 
{ 
    public int Type2ID { get; set; } 
    public int Type2MasterID { get; set; } 

    public String Notes { get; set; } 
    public decimal Price { get; set; } 
} 

Dann wird der ursprüngliche Code wird :

if (condition1) 
{ 
    foreach (var type1 in type1Collection) 
    { 
     type1.MyLogic(); 
    } 
} 
else 
{ 
    foreach (var type2 in type2Collection) 
    { 
     type2.MyLogic(); 
    } 
} 
+0

Hier ist keine Erweiterungsmethode erforderlich. Sobald Sie die Schnittstelle haben, können Sie einfach über die gewünschte Sammlung iterieren, wobei Sie jeden Wert als Schnittstelle verwenden.Natürlich wissen wir nicht, ob das OP die Entitätstypen ändern kann. –

+0

Wahr, aber die Erweiterungsmethode ermöglicht es, alle Zuweisungen an einer Stelle zu halten (zusammen mit jedem anderen Code, den das OP im Interesse der Lesbarkeit verwendet hat). –

+2

Ich würde es vorziehen, 'CONcat' von LINQ zu verwenden, um nur eine Schleife mit der Logik an einem Ort zuzulassen - wo dieser Ort genau dort ist, wo Sie die Arbeit machen. –

Verwandte Themen