如何将两种类型的C#列表合并为一个?
我创建了两个名为X&Y的列表。这两个列表的类型不同。 (即List X
& List Y
)。
这两个列表中的值都不同。 但是这两个列表中都有一个DateTime
字段。
我需要根据date
字段对这些列表进行排序。
我有单独的函数来打印列表A和列表B的详细信息。
假设排序列表看起来像
我的目的是遍历此列表并调用相应的函数来显示详细信息。 即,如果元组来自列表A,则调用函数打印列表A的详细信息,反之亦然。
您可以创建一个interface
来托管您的公共属性和函数,然后在转换到此接口后加入这些列表并对其进行排序:
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 { new ClassA() { DT = DateTime.Now.AddDays(-1) }, new ClassA() { DT = DateTime.Now.AddDays(-3) }}; var listB = new List { new ClassB() { DT = DateTime.Now.AddDays(-2) }, new ClassB() { DT = DateTime.Now.AddDays(-4) }}; var orderedResult = listA.Cast() .Concat(listB.Cast ()) .OrderBy(q => q.DT); //or possibly even better: //var orderedResult = listA.Concat (listB).OrderBy(q => q.DT); foreach (var obj in orderedResult) obj.Print(); }
如果您可以更改 A
类和B
类的定义 ,那么您应该使用其中一个其他答案,这些答案告诉您为这两个类添加一个接口。
但是, 如果您无法更改 A
类和B
类的定义 (可能是因为它们是在您无法更改的库中定义的),那么您必须采用不同的方法。
一个保持相当整洁的解决方案是引入一个包装类,它包含A
类或B
类(但不是两者)的实例。
假设您的类看起来像这样:
class A { public DateTime Date; public double Value; public void Print() {} } class B { public DateTime Date; public string Value; public void Print() { } }
您可以编写一个如下所示的简单包装器:
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(); } }
然后给出两个列表:
var listA = new List(); var listB = new List();
您可以创建Wrapper
对象的排序列表:
var sorted = listA.Select(a => new Wrapper(a)) .Concat(listB.Select(b => new Wrapper(b))) .OrderBy(item => item.Date);
然后为排序列表中的每个项调用适当的Print()
方法:
foreach (var item in sorted) item.Print();
如果基于Base class
创建Class A
和Class B
Base class
,则可以使它们从Base class
派生DateTime
属性。
例如:
public class BaseClass { public DateTime date {get; set; } } public class ClassA : BaseClass { //... some other property's } public class ClassB : BaseClass { //... some other property's }
在你的代码中:
List a = new List(); List b = new List();
并且因为ClassA和ClassB基于BaseClass,它们将具有相同的date
属性。
您可以为您拥有的2个类创建一个简单的界面:
public interface interfaceA { DateTime TimeStamp { get;set; } }
并确保您的类都实现该接口:
public class class_A : interfaceA { public DateTime TimeStamp { get; set; } //... Other properties } public class class_B : interfaceA { public DateTime TimeStamp { get; set; } //... Other properties }
在你的主函数/方法中,你创建一个包含两种对象类型的新List并填充它 – 而不是填写你的2个列表中的一个,你只需填写这一个列表:
var myList = new List(); var object = new class_A() { TimeStamp = DateTime.Now }; myList.Add(object);
然后对它进行排序:
myList.Sort((a, b) => a.TimeStamp.CompareTo(b.TimeStamp))
我觉得这样的事情应该有效
无需创建新的接口或类。 您可以同时遍历两个列表并跟踪最近的日期。
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(); var Y = new List(); 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; } } } }
样本输出:
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
在原始A和B类型不兼容且不可更改的情况下(可能它们来自某些类型不相关的数据库),您还可以使用LINQ进行投影以合并它们。 从本质上讲,这创造了第三类,尽管是匿名的。 通常,当您可以修改类并使用基类或接口方法时,应该避免使用此方法,但是,由于这并非总是可行,因此下面是使用LINQ投影的示例。 请注意,此示例中的原始类型具有不同的日期时间名称,可以在投影期间“合并”。
class Program { static void Main(string[] args) { List aList = new List {new A {OtherAData = "First A", SomeDateTime = DateTime.Parse("12-11-1980")}, new A {OtherAData = "Second A", SomeDateTime = DateTime.Parse("12-11-2000")} }; List bList = new List {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); } }
样本输出:
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
那么,除了接口解决方案,你可以使用抽象类,这个类可以包含方法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") { } }
用法:
var commonList = listA.Concat(listB.Cast()).OrderBy(e => e.DT).ToList(); commonList.ForEach(obj => obj.Print());
如果您不想创建基本类型或接口,可以执行以下操作:
new List> { listA, listB } .SelectMany(x => x) .OrderBy(x => x.Date).ToList() .ForEach(x => Print(x));
你显然需要一个Print()
函数,为class_A
和class_B
。
使Print()
成为成员函数或扩展方法会使上面的代码更整洁。
创建具有其他两个属性的第三个类。 然后创建其对象列表并按日期时间对其进行排序。
我建议使用带有Action
委托的匿名类型的更简单方法。 无需更改A和B的定义,无需引入额外的类/接口。
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(); } } }
如果我遇到问题,那么我会使用这样的代理类:
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)); }
但我宁愿让印刷方法来决定谁是谁
首先,通过其属性对第一个进行排序,然后将其强制转换为object,然后将其与其属性排序的第二个列表连接:
var lists = listA .OrderBy(x => x.DateTimeProp) .OfType
在此之后,您可以遍历所有项目并根据每个项目的类型进行操作。 当然,这会对每个列表进行独立排序。 如果需要全局排序,则需要基类或公开可排序属性的共享接口。
上述就是C#学习教程:如何将两种类型的C#列表合并为一个?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/1010840.html