我正在尝试比较List1和List2 [List2是经过编辑的List1],然后生成仅包含更新的列和更新的值的列表
列表1
var x = (from a in table1 where id == 1 select a).firstordefault();
在下面产生
ID | Val1 | Val2 | Val3 | Val4 | Val5
-------------------------
1 | A | B | C | D | E
ID = 1的项目更新后
列表2
ID | Val1 | Val2 | Val3 | Val4 | Val5
-------------------------
1 | A | C | C | F | E
然后比较列表1 和列表2 以生成输出列表
输出列表
ID | Val2 | Val4 |
-------------------
1 | C | F |
答案 0 :(得分:0)
public class Difference
{
public int Id {get;set;}
public string Val1 {get;set;}
public string Val2 {get;set;}
public string Val3 {get;set;}
public string Val4 {get;set;}
public string Val5 {get;set;}
}
var result = new List<Difference>();
var props = typeof(Difference).GetProperties();
foreach(var origin in source)
{
var against = updated.FirstOrDefault(x => x.Id == origin.Id);
if(against != null)
{
var diff = new Difference{ Id = origin.Id };
var add = false;
foreach(var prop in props)
{
var againstVal = prop.GetValue(against);
if(prop.GetValue(origin) != againstVal)
{
prop.SetValue(diff, againstVal)
add = true;
}
}
if(add)
result.Add(diff);
}
}
foreach(var res in result)
{
foreach(var prop in props)
{
var val = prop.Value;
if(val != null)
Console.Write($"{prop.Name}:{prop.Value} ")
}
Console.WriteLine();
}
//output:
//Id:1 Val2:C Val4:F
//Id:2 Val2:C Val3:F Val13:S
//...
答案 1 :(得分:0)
您自己变得很困难,只希望获得有关已更改的属性值的信息。这使得结果中的每一行都有不同数量的值。
除了您忘记告诉我们Id
列中的值是列表中项目的索引还是每个列表元素都具有属性Id
。
此外:是列表的类型在编译时已经知道还是仅在运行时知道。换句话说,它是List<TSource>
,还是更像System.Data.DataTable
?
另一个问题:ID是否唯一?并且如果列表2的元素的ID = 5,但列表1的行没有此ID,您是否希望它在结果中作为附加值?如果列表1的ID在列表2中不存在,是否为已删除的值?
顺便说一句,我讨厌人们对此做出反应:“哦,对不起,我 忘记提及ID实际上是列表中项目的索引,哦,我 还忘了说我的两个列表长度相等,你不能 从列表中添加或删除元素。 规格,然后问一个问题!
List<TSource>
此解决方案在编译时检测错误。如果您尝试在没有Id
的情况下将项目放入列表,则编译器将无法编译。如果您不小心尝试将DateTime与字符串等进行比较,则会出现错误。但是,问题在于您必须事先知道列表中有哪些项目。
IEnumerable<TSource> ListA = ...
IEnumerable<TSource> ListB = ...
为确保您的TSource
具有Id
的概念,我不会创建输入为List<TSource>
而是List> , where the
int { {1}}列表中的TSource`或其中一个属性。
从is the Id. This can be the index of the
到List>`的转换很简单:
List<TSource>
现在我们已经确定了ID,我们可以给出函数的签名
// The index is the Id
static IEnumerable<KeyValuePair<int, TSource> ToIndexedPair<TSource>(
this IEnumerable<TSource> source)
{
return source.Select( (source, index) =>
new KeyValuePair<int, TSource>(index, source));
}
// The index is one of the properties:
static IEnumerable<KeyValuePair<int, TSource> ToIndexedPair<TSource>(
this IEnumerable<TSource> source,
Func<TSource, int> IdSelector)
{
return source.Select(source => new KeyValuePair<int, TSource>
(IdSelector(source), source));
}
作为输出,您需要一个对象序列,其中包含一个IEnumerable<...> ExtractDifferences<TSource>(
IEnumerable<KeyValuePair<int, TSource> listA,
IEnumerable<KeyValuePair<int, TSource> listB)
{
...
}
和一个序列Id
,其中每个DiffInfo
包含有关更改后的值的信息:已更改,并且更改了值。为了娱乐,我还将添加原始值。
问题是:结果中,您需要具有更改后的值和更改后的属性的序列。这些值可以是DiffInfo
,Datetime
或int
或任何其他类。因此,在返回序列中,我对列表中的属性的全部了解是它们是string
并且它们具有值。除非您对objects
有更多了解,否则您不能做太多事,因此我也提供objects
PropertyInfo
注意,通过以这种方式定义类,我只需要存储一些指针和class DiffPropertyInfo<TSource>
{
public int Id {get; set;}
public TSource OriginalValue {get; set;}
public TSource AlternativeValues {get; set;}
public PropertyInfo PropertyInfo {get; set;}
public object OriginalPropertyValue
{
get {return this.PropertyInfo.GetValue(this.OriginalValue);}
}
public object AlternativePropertyValue
{
get { return this.PropertyInfo.GetValue(this.AlternativeValue); }
}
public bool IsChanged()
{
object originalPropertyValue = this.OriginalPropertyValue;
object alternativePropertyValue = this.AlternativePropertyValue;
return Object.Equals(originalPropertyValue, alternativePropertyValue);
}
}
。直到您真正询问它的值是否已更改,才使用价格昂贵的GetPropertyValue。
顺便说一句:使用Id
,而不是Object.Equals
,因为如果获取的对象是值类型,则我们需要正确的X == Y
的重写版本。另外,如果您有一个覆盖Object.Equals的类,则需要该相等性,而不是默认的Equals
。
提取两个TSource对象之间的不同属性:
Object.Equals
对于您输入序列中的每个元素,我们都需要存储差异的结果:
IEnumerable<DiffPropertyInfo> ExtractProperties(int Id, TSource original, TSource alternative)
{
// examine only readable properties that can be changed:
IEnumerable<PropertyInfo> propertyInfos = typeof(TSource)
.GetProperties()
.Where(property => property.CanRead && property.CanWrite);
// the following can be done as one LINQ statement
// for readability I'll use yield return
foreach (PropertyInfo propertyInfo in propertyInfos
{
yield return new DiffPropertyInfo()
{
Id = Id,
OriginalValue = original,
AlternativeValue = alternative,
PropertyInfo = propertyInfo,
};
}
}
在这里,您会看到相同的方法:只有在有人要求时才计算差异。
因此,现在我们可以返回将执行您想要的操作的原始功能
class PropertyComparisonCollection<TSource>
{
public int Id {get; set;}
public TSource OriginalValue {get; set;}
public TSource AlternativeValues {get; set;}
// returns a sequence of changed properties:
public IEnumerable<DiffPropertyInfo> GetChangedProperties()
{
// Use the function ExtractProperties defined above
return Extractproperties(this.OriginalValue, this.AlternativeValue)
.Where(extractedpropery => extractedProperty.IsChanged());
}
}
结果是IEnumerable<...> ExtractDifferences<TSource>(
IEnumerable<KeyValuePair<int, TSource> listA,
IEnumerable<KeyValuePair<int, TSource> listB)
{
// The Id is in the Key of the KeyValuePair
// inner join on same Id and get Id / OriginalValue / AlternativeValue
// then Create PropertyComparisonCollection per Id / OriginalValue / AlternativeValue
// Finally per Id return the properties that are changed
return listA.Join(listB, // join ListA and ListB
listA => listA.Key, // from every ListA take the Key (which is Id)
listB => listB.Key, // from every ListB take the Key
(listA, listB) => new PropertyComparisonCollection() // when they match
{ // make one new object
Id = listA.Key,
OriginalValue = listA.Value,
AlternativeValue = listB.Value,
})
// Keep only the Ids that have at least one changed value
.Where(collectionElement => collectionElement.GetChangedProperties().Any());
}
的序列。序列中的每个元素都包含DiffPropertyInfo
,Id
和OriginalValue
。您可以向每个元素询问已更改的属性。
如果您真的只希望Id和更改的属性值(如什么?作为字符串?作为对象?),请使用GetChangedProperties:
AlternativeValue
但是,那样您将丢失很多信息。我会去寻找原始的返回集合,您可以在其中轻松找到更改后的值。
顺便说一句,您是否注意到,直到知道我没有列举?您的序列尚未被访问。没有进行任何比较,没有枚举PropertyInfo,等等。一旦您请求第一个元素,就会枚举PropertyInfo,并获取第一个值。
注意:有些人不喜欢在各个班级中进行这种分离。如果需要,可以将所有内容合并为一个大的LINQ语句。我不确定这是否会提高可读性,可重用性,可维护性和可测试性。