J'ai créé deux listes dites X & Y. Ces deux listes sont de types différents. (ie List<class_A> X
& List<class_B> Y
).
Les valeurs dans ces deux listes sont différentes. Mais il existe un champ DateTime
dans ces deux listes.
Je dois trier ces listes en fonction du champ date
.
J'ai différentes fonctions pour imprimer les détails de la liste A et de la liste B.
Supposons que la liste triée ressemble à
Mon but est de parcourir cette liste en boucle et d’appeler la fonction appropriée pour afficher les détails. c'est-à-dire que si le tuple provient de la liste A, appelez la fonction d'impression des détails de la liste A et inversement.
Vous pouvez créer une interface
pour héberger vos propriétés et fonctions communes, puis joindre ces listes après la conversion sur cette interface et la trier ensuite:
interface ICommon
{
DateTime DT { get; }
void Print();
}
class ClassA : ICommon
{
public DateTime DT { get; set; }
public void Print() { Console.WriteLine("A " + DT); }
}
class ClassB : ICommon
{
public DateTime DT { get; set; }
public void Print() { Console.WriteLine("B " + DT); }
}
public static void Main()
{
var listA = new List<ClassA> { new ClassA() { DT = DateTime.Now.AddDays(-1) }, new ClassA() { DT = DateTime.Now.AddDays(-3) }};
var listB = new List<ClassB> { new ClassB() { DT = DateTime.Now.AddDays(-2) }, new ClassB() { DT = DateTime.Now.AddDays(-4) }};
var orderedResult = listA.Cast<ICommon>()
.Concat(listB.Cast<ICommon>())
.OrderBy(q => q.DT);
//or possibly even better:
//var orderedResult = listA.Concat<ICommon>(listB).OrderBy(q => q.DT);
foreach (var obj in orderedResult)
obj.Print();
}
Si vous pouvez modifier la définition de la classe A
et de la classe B
, vous devez utiliser l'une des autres réponses ci-dessous, qui vous invite à ajouter une interface à ces deux classes.
Toutefois, si vous ne pouvez pas modifier la définition de la classe A
et de la classe B
(peut-être parce qu'elles sont définies dans une bibliothèque que vous ne pouvez pas modifier), vous devez adopter une approche différente.
Une solution qui garde les choses bien ordonnées consiste à introduire une classe wrapper qui contient une instance de la classe A
ou de la classe B
(mais pas les deux).
Supposons que vos classes ressemblent à ceci:
class A
{
public DateTime Date;
public double Value;
public void Print() {}
}
class B
{
public DateTime Date;
public string Value;
public void Print() { }
}
Vous pouvez écrire un simple wrapper qui ressemble à ceci:
class Wrapper
{
readonly A a;
readonly B b;
public Wrapper(A a)
{
this.a = a;
}
public Wrapper(B b)
{
this.b = b;
}
public DateTime Date => a?.Date ?? b.Date;
public void Print()
{
if (a != null)
a.Print();
else
b.Print();
}
}
Puis donné deux listes:
var listA = new List<A>();
var listB = new List<B>();
Vous pouvez créer une liste triée d'objets Wrapper
:
var sorted =
listA.Select(a => new Wrapper(a))
.Concat(listB.Select(b => new Wrapper(b)))
.OrderBy(item => item.Date);
Ensuite, pour appeler la méthode Print()
appropriée pour chaque élément de la liste triée:
foreach (var item in sorted)
item.Print();
Vous pouvez créer une interface simple pour les 2 classes que vous avez:
public interface interfaceA {
DateTime TimeStamp { get;set; }
}
Et assurez-vous que vos deux classes implémentent cette interface:
public class class_A : interfaceA {
public DateTime TimeStamp { get; set; }
//... Other properties
}
public class class_B : interfaceA {
public DateTime TimeStamp { get; set; }
//... Other properties
}
dans votre fonction/méthode principale, vous créez une nouvelle liste qui contient les deux types d’objets et vous la remplissez. Au lieu de remplir l’une de vos deux listes, vous ne remplissez que cette liste:
var myList = new List<interfaceA>();
var object = new class_A() { TimeStamp = DateTime.Now };
myList.Add(object);
Et puis le trier:
myList.Sort((a, b) => a.TimeStamp.CompareTo(b.TimeStamp))
Je pense que quelque chose comme ça devrait marcher
Si vous créez vos Class A
et Class B
sur la base d'un Base class
, vous pouvez leur demander de dériver votre propriété DateTime
à partir de Base class
.
par exemple:
public class BaseClass
{
public DateTime date {get; set; }
}
public class ClassA : BaseClass
{
//... some other property's
}
public class ClassB : BaseClass
{
//... some other property's
}
dans votre code:
List<BaseClass> a = new List<ClassA>();
List<BaseClass> b = new List<ClassB>();
Etant donné que ClassA et ClassB sont basées sur BaseClass, elles auront la même propriété date
.
Pas besoin de créer de nouvelles interfaces ou classes. Vous pouvez simplement parcourir les deux listes en même temps et suivre la date la plus récente.
public class TypeA
{
public DateTime date { get; set; }
public void Print() { Console.WriteLine("TypeA: " + date.ToString()); }
}
public class TypeB
{
public DateTime date { get; set; }
public void Print() { Console.WriteLine("TypeB: " + date.ToString()); }
}
class Program
{
static void Main(string[] args)
{
// setup
var X = new List<TypeA>();
var Y = new List<TypeB>();
Random rnd = new Random();
int imin, imax;
imin = rnd.Next(3, 7);
imax = rnd.Next(10, 20);
for (int i = imin; i < imax; i++)
{
X.Add(new TypeA() { date = DateTime.Now.AddDays(-1 * rnd.Next(1, 1000)) });
}
imin = rnd.Next(3, 7);
imax = rnd.Next(10, 20);
for (int i = imin; i < imax; i++)
{
Y.Add(new TypeB() { date = DateTime.Now.AddDays(-1 * rnd.Next(1, 1000)) });
}
X = X.OrderBy(z => z.date).ToList();
Y = Y.OrderBy(z => z.date).ToList();
// begin print in order
// index for X list
int ix = 0;
// index for Y list
int iy = 0;
// most recently printed date
DateTime min = DateTime.MinValue;
while (true)
{
if (ix < X.Count && X[ix].date >= min && (iy >= Y.Count || iy < Y.Count && X[ix].date <= Y[iy].date))
{
X[ix].Print();
min = X[ix].date;
ix++;
continue;
}
if (iy < Y.Count && Y[iy].date >= min)
{
Y[iy].Print();
min = Y[iy].date;
iy++;
}
if (ix >= X.Count && iy >= Y.Count)
{
break;
}
}
}
}
Exemple de sortie:
TypeB: 12/19/2013 3:44:44 PM
TypeB: 2/19/2014 3:44:44 PM
TypeB: 5/1/2014 3:44:44 PM
TypeA: 5/27/2014 3:44:44 PM
TypeA: 6/6/2014 3:44:44 PM
TypeA: 7/12/2014 3:44:44 PM
TypeA: 7/18/2014 3:44:44 PM
TypeB: 12/5/2014 3:44:44 PM
TypeB: 5/3/2015 3:44:44 PM
TypeB: 5/4/2015 3:44:44 PM
TypeB: 8/9/2015 3:44:44 PM
TypeA: 8/25/2015 3:44:44 PM
TypeA: 9/20/2015 3:44:44 PM
TypeB: 9/26/2015 3:44:44 PM
TypeA: 10/12/2015 3:44:44 PM
TypeA: 12/7/2015 3:44:44 PM
TypeB: 12/19/2015 3:44:44 PM
TypeA: 4/13/2016 3:44:44 PM
TypeA: 5/23/2016 3:44:44 PM
TypeB: 8/4/2016 3:44:44 PM
TypeB: 8/11/2016 3:44:44 PM
Dans le cas où les types A et B d'origine sont incompatibles et non modifiables (ils proviennent peut-être d'une base de données où les types ne sont pas liés), vous pouvez également utiliser une projection utilisant LINQ pour les fusionner. Cela crée, en substance, une troisième classe, même anonyme. En général, vous devriez éviter cette approche lorsque vous pouvez modifier les classes et utiliser l'approche de classe ou d'interface de base. Toutefois, comme cela n'est pas toujours possible, voici un exemple d'utilisation d'une projection LINQ. Notez que les types originaux dans cet exemple ont des noms différents pour le datetime qui peuvent être "fusionnés" pendant la projection.
class Program
{
static void Main(string[] args)
{
List<A> aList = new List<A> {new A {OtherAData = "First A", SomeDateTime = DateTime.Parse("12-11-1980")},
new A {OtherAData = "Second A", SomeDateTime = DateTime.Parse("12-11-2000")} };
List<B> bList = new List<B> {new B {OtherBData = "First B", SomeOtherDateTime = DateTime.Parse("12-11-1990")},
new B {OtherBData = "Second B", SomeOtherDateTime = DateTime.Parse("12-11-2010")} };
// create projections
var unionableA = from a in aList
select new {SortDateTime = a.SomeDateTime, AValue = a, BValue = (B) null};
var unionableB = from b in bList
select new {SortDateTime = b.SomeOtherDateTime, AValue = (A) null, BValue = b};
// union the two projections and sort
var union = unionableA.Union(unionableB).OrderBy(u => u.SortDateTime);
foreach (var u in union)
{
if (u.AValue != null)
{
Console.WriteLine("A: {0}",u.AValue);
}
else if (u.BValue != null)
{
Console.WriteLine("B: {0}",u.BValue);
}
}
}
}
public class A
{
public DateTime SomeDateTime { get; set; }
public string OtherAData { get; set; }
public override string ToString()
{
return string.Format("SomeDateTime: {0}, OtherAData: {1}", SomeDateTime, OtherAData);
}
}
public class B
{
public DateTime SomeOtherDateTime { get; set; }
public string OtherBData { get; set; }
public override string ToString()
{
return string.Format("SomeOtherDateTime: {0}, OtherBData: {1}", SomeOtherDateTime, OtherBData);
}
}
Exemple de sortie:
A: SomeDateTime: 12/11/1980 12:00:00 AM, OtherAData: First A
B: SomeOtherDateTime: 12/11/1990 12:00:00 AM, OtherBData: First B
A: SomeDateTime: 12/11/2000 12:00:00 AM, OtherAData: Second A
B: SomeOtherDateTime: 12/11/2010 12:00:00 AM, OtherBData: Second B
En dehors de la solution d’interface, vous pouvez utiliser une classe abstraite et cette classe peut contenir l’implémentation par défaut de la méthode Print:
abstract class AbstractClass
{
protected string Details { get; set; }
public DateTime DT { get; set; }
private AbstractClass() { }
protected AbstractClass(string details) { Details = details;}
public virtual void Print()
{
Console.WriteLine($"{Details} {DT}");
}
}
internal class ClassA : AbstractClass
{
public ClassA() : base("A") { }
}
class ClassB : AbstractClass
{
public ClassB() : base("B") { }
}
Utilisation:
var commonList = listA.Concat(listB.Cast<AbstractClass>()).OrderBy(e => e.DT).ToList();
commonList.ForEach(obj => obj.Print());
Si vous ne voulez pas créer un type de base ou une interface, vous pouvez faire quelque chose comme ceci:
new List<IEnumerable<dynamic>> { listA, listB }
.SelectMany(x => x)
.OrderBy(x => x.Date).ToList()
.ForEach(x => Print(x));
Vous aurez évidemment besoin d’une fonction Print()
, surchargée pour class_A
et class_B
.
Faire de Print()
une fonction membre ou une méthode d'extension rendrait le code ci-dessus encore plus net.
Créez une troisième classe ayant les propriétés des deux autres. Créez ensuite une liste de ses objets et triez-la par date/heure.
si le problème est résolu correctement, j'utiliserais une classe de proxy comme celle-ci:
public class Proxy
{
public object obj { get; set; }
public DateTime date { get; set; }
}
var listAll = listA.Select(a => new Proxy { obj = a, date = a.date }).Union(listB.Select(b => new Proxy { obj = b, date = b.date })).OrderBy(d=>d.date).ToList();
listAll.ForEach(c =>
{
if (c.obj.GetType() == typeof(A))
printA(((A)c.obj));
else if (c.obj.GetType() == typeof(B))
printB(((B)c.obj));
}
mais je préférerais laisser la méthode d'impression décider qui est qui
Je suggérerais une manière plus simple d'utiliser un type anonyme avec un délégué Action
. Pas besoin de changer la définition de A et B, pas besoin d'introduire des classes/interfaces supplémentaires.
class A
{
public DateTime Date;
public void Print() { Console.Write("A"); }
}
class B
{
public DateTime Date;
public void Print() { Console.Write("B"); }
}
class Program
{
static void Main(string[] args)
{
// Generate sample data
var r = new Random();
var listA = Enumerable.Repeat(0, 10)
.Select(_ => new A { Date = new DateTime(r.Next()) })
.ToList();
var listB = Enumerable.Repeat(0, 10)
.Select(_ => new B { Date = new DateTime(r.Next()) })
.ToList();
// Combine the two lists into one of an anonymous type
var combined = listA.Select(a => new { a.Date, Print = new Action(() => a.Print()) })
.Concat(listB.Select(b => new { b.Date, Print = new Action(() => b.Print()) }))
.OrderBy(c => c.Date)
;
foreach (var item in combined)
{
item.Print();
Console.Write(": " + item.Date);
Console.WriteLine();
}
}
}