我创建了两个名单,分别是X& Y.这两个列表是不同类型的。
(即List<class_A> X
&amp; List<class_B> Y
)。
这两个列表中的值都不同。但是这两个列表中都有一个DateTime
字段。
我需要根据date
字段对这些列表进行排序。
我有单独的函数来打印列表A和列表B的详细信息。
假设排序列表看起来像
我的目的是遍历此列表并调用相应的函数来显示详细信息。即,如果元组来自列表A,则调用函数打印列表A的详细信息,反之亦然。
答案 0 :(得分:27)
您可以创建一个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<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();
}
答案 1 :(得分:22)
如果您可以更改类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<A>();
var listB = new List<B>();
您可以创建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();
答案 2 :(得分:7)
如果您根据Class A
制作Class B
和Base class
,则可以从DateTime
获取Base class
属性。
例如:
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<BaseClass> a = new List<ClassA>();
List<BaseClass> b = new List<ClassB>();
鉴于ClassA和ClassB基于BaseClass,它们将具有相同的date
属性。
答案 3 :(得分:6)
您可以为您拥有的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<interfaceA>();
var object = new class_A() { TimeStamp = DateTime.Now };
myList.Add(object);
然后对其进行排序:
myList.Sort((a, b) => a.TimeStamp.CompareTo(b.TimeStamp))
我认为这样的事情应该有效
答案 4 :(得分:5)
无需创建新的接口或类。您可以同时遍历两个列表并跟踪最近的日期。
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;
}
}
}
}
示例输出:
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
答案 5 :(得分:2)
如果原始A和B类型不兼容且不可更改(可能它们来自某些类型不相关的数据库),您还可以使用LINQ进行投影以合并它们。从本质上讲,这创造了第三类,尽管是匿名的。通常,当您可以修改类并使用基类或接口方法时,应该避免使用此方法,但是,由于这并非总是可行,因此下面是使用LINQ投影的示例。请注意,此示例中的原始类型具有不同的日期时间名称,可以在投影期间“合并”。
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);
}
}
示例输出:
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
答案 6 :(得分:1)
嗯,除了接口解决方案之外,你可以使用抽象类,这个类可以包含方法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<AbstractClass>()).OrderBy(e => e.DT).ToList();
commonList.ForEach(obj => obj.Print());
答案 7 :(得分:1)
如果您不想创建基本类型或界面,可以执行以下操作:
new List<IEnumerable<dynamic>> { listA, listB }
.SelectMany(x => x)
.OrderBy(x => x.Date).ToList()
.ForEach(x => Print(x));
您显然需要一个Print()
函数,已为class_A
和class_B
重载。
使Print()
成员函数或扩展方法可以使上面的代码更整洁。
答案 8 :(得分:0)
创建具有其他两个属性的第三个类。然后创建其对象列表并按日期时间对其进行排序。
答案 9 :(得分:0)
我建议使用带有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();
}
}
}
答案 10 :(得分:0)
如果我遇到问题,那么我会使用这样的代理类:
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));
}
但我宁愿让印刷方法决定谁是谁
答案 11 :(得分:-2)
首先,通过其属性对第一个进行排序,然后将其强制转换为对象,然后将其与其属性排序的第二个列表连接起来:
var lists = listA
.OrderBy(x => x.DateTimeProp)
.OfType<object>()
.Concat(
listB
.OrderBy(x => x.DateTimeProp)
.OfType<object>()
);
在此之后,您可以遍历所有项目并根据每个项目的类型进行操作。 当然,这会对每个列表进行独立排序。如果需要全局排序,则需要基类或公开可排序属性的共享接口。