通过合并其他三个具有优先级的对象来创建对象 (C# .NET 3.5)
Create object by merging other three with priority (C# .NET 3.5)
我有三个相同 class(A、B、C)的 C#(将 .NET 3.5 用于遗留软件)对象,它们具有所有 public 属性(string、int、short , 字节, 日期时间, 双)
我需要通过合并 "three" 个对象来创建第四个 (D)。
如果 A 有一个 属性 集(非空或空),我必须在 D 中设置它,否则我必须检查 B,然后作为最后一个 C。
最有效和优雅的方法是什么?
我读过反射是正确的方法吗?
反思是一种方法。下面是一个例子;可能不是最优雅的,但它可以建立在:
using System
using System.Reflection;
namespace Test
{
class Program
{
static void Main(string[] args)
{
Car A = new Car
{
Make = "Volvo"
};
Car B = new Car
{
Year = 2001,
CreateDate = DateTime.Now
};
Car C = new Car
{
ShortValue = 1,
MSRP = 20000,
ByteValue = 10
};
Car D = new Car();
Mapper mapobj = new Mapper();
D = mapobj.Map<Car>(A);
D = mapobj.Compare<Car>(B, D);
D = mapobj.Compare<Car>(C, D);
// Car D now has all the initialized properties of A,B,C
}
public class Mapper
{
public T Map<T>(T data)
{
var _result = (T)Activator.CreateInstance(typeof(T), new object[] { });
foreach (PropertyInfo propertyInfo in typeof(T).GetProperties())
{
if (typeof(T).GetProperty(propertyInfo.Name) != null)
typeof(T)
.GetProperty(propertyInfo.Name,
BindingFlags.IgnoreCase |
BindingFlags.Instance |
BindingFlags.Public)
.SetValue(_result,
propertyInfo.GetValue(data));
}
return _result;
}
public T Compare<T>(T data, T data2)
{
var _result = (T)Activator.CreateInstance(typeof(T), new object[] { });
foreach (PropertyInfo propertyInfo in typeof(T).GetProperties())
{
if (typeof(T).GetProperty(propertyInfo.Name) != null)
{
bool isnullvalue = false;
DateTime zerodate = new DateTime();
switch (propertyInfo.PropertyType.Name)
{
case "String":
if ((string)propertyInfo.GetValue(data) != null && (string)propertyInfo.GetValue(data2) == null)
isnullvalue = true;
break;
case "Int32":
if ((Int32)propertyInfo.GetValue(data) != 0 && (Int32)propertyInfo.GetValue(data2) == 0)
isnullvalue = true;
break;
case "Int16":
if ((Int16)propertyInfo.GetValue(data) != 0 && (Int16)propertyInfo.GetValue(data2) == 0)
isnullvalue = true;
break;
case "Byte":
if ((Byte)propertyInfo.GetValue(data) != 0 && (Byte)propertyInfo.GetValue(data2) == 0)
isnullvalue = true;
break;
case "Double":
if ((Double)propertyInfo.GetValue(data) != 0 && (Double)propertyInfo.GetValue(data2) == 0)
isnullvalue = true;
break;
case "DateTime": // DateTime.Compare(date1, date2)
DateTime time1 = (DateTime)propertyInfo.GetValue(data);
DateTime time2 = (DateTime)propertyInfo.GetValue(data2);
if (DateTime.Compare(time1, zerodate) != 0 && DateTime.Compare(time2, zerodate) == 0)
isnullvalue = true;
break;
default:
Console.WriteLine("No handler for type {} found");
Console.ReadLine();
Environment.Exit(-1);
break;
}
if (isnullvalue)
{
typeof(T).GetProperty(propertyInfo.Name,
BindingFlags.IgnoreCase |
BindingFlags.Instance |
BindingFlags.Public)
.SetValue(_result,
propertyInfo.GetValue(data));
}
else
{
typeof(T).GetProperty(propertyInfo.Name,
BindingFlags.IgnoreCase |
BindingFlags.Instance |
BindingFlags.Public)
.SetValue(_result,
propertyInfo.GetValue(data2));
}
}
}
return _result;
}
}
public class Car
{
public string Make { get; set; }
public int Year { get; set; }
public short ShortValue { get; set; }
public byte ByteValue { get; set; }
public DateTime CreateDate { get; set; }
public double MSRP { get; set; }
}
}
}
毫无疑问,您可以在这里使用基于反射的解决方案,但您可能不需要它。如果你提前知道合并的类型,你可以写一个非常简单的映射函数来处理这个。
例如,给定一个简单的 class...
class MyClassToMap
{
public string MyString { get; set; }
public int MyInt { get; set; }
}
你可以写一个简单的方法...
MyClassToMap Map(params MyClassToMap[] toMap)
{
var mapped = new MyClassToMap();
foreach (var m in toMap)
{
// 'default' is shorthand for a type's uninitalized value. In the case of
// string, it resolves to "null", and in the case of int, it resolves to 0.
// You could also use the literal values here, if you prefer.
// Note that for C# versions < 7.1, you must specify the type--eg "default(string)".
// See: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/default
if (m.MyString != default) mapped.MyString = m.MyString;
if (m.MyInt != default) mapped.MyInt = m.MyInt;
}
return mapped;
}
然后这样称呼它...
var a = new MyClassToMap { MyString = "foo", MyInt = 0 };
var b = new MyClassToMap { MyString = "bar", MyInt = 100 };
var c = new MyClassToMap { MyString = null, MyInt = 0 };
var mapped = Map(a, b, c);
Console.WriteLine($"MyString = {mapped.MyString}, MyInt = {mapped.MyInt}");
// prints: { MyString = "bar", MyInt = 100 };
您可以为此目的使用反射。例如,请检查以下代码(添加到解释方法的方法中的内联注释)
public T AssignProperties<T>(params T[] sources)
{
// Types of properties that has to be copied
var filteredPropertyTypes = new []{typeof(int),typeof(string),typeof(short),typeof(byte),typeof(DateTime),typeof(double)};
// Create Default Instance of T
var newInstance = (T)Activator.CreateInstance(typeof(T));
// Iterate through each property
foreach(var property in typeof(T).GetProperties().Where(x=>filteredPropertyTypes.Contains(x.PropertyType)))
{
// Get the default Value of the Type and get the first instance in sources, which has a non-default value for the property
var defaultValue = property.PropertyType.IsValueType ? Convert.ChangeType(Activator.CreateInstance(property.PropertyType),property.PropertyType) : null;
if(sources.Any(x=>!Convert.ChangeType(property.GetValue(x),property.PropertyType).Equals(defaultValue)))
{
var newValue = property.GetValue(sources.First(x=>!Convert.ChangeType(property.GetValue(x),property.PropertyType).Equals(defaultValue)));
property.SetValue(newInstance,newValue);
}
}
return newInstance;
}
您现在可以将该方法用于 N 个实例(A、B、C...),实例按需要处理的顺序传递。
var newInstance = AssignProperties(instanceA,instanceB,instanceC);
我有三个相同 class(A、B、C)的 C#(将 .NET 3.5 用于遗留软件)对象,它们具有所有 public 属性(string、int、short , 字节, 日期时间, 双) 我需要通过合并 "three" 个对象来创建第四个 (D)。 如果 A 有一个 属性 集(非空或空),我必须在 D 中设置它,否则我必须检查 B,然后作为最后一个 C。 最有效和优雅的方法是什么? 我读过反射是正确的方法吗?
反思是一种方法。下面是一个例子;可能不是最优雅的,但它可以建立在:
using System using System.Reflection;namespace Test { class Program { static void Main(string[] args) { Car A = new Car { Make = "Volvo" };
Car B = new Car { Year = 2001, CreateDate = DateTime.Now }; Car C = new Car { ShortValue = 1, MSRP = 20000, ByteValue = 10 }; Car D = new Car(); Mapper mapobj = new Mapper(); D = mapobj.Map<Car>(A); D = mapobj.Compare<Car>(B, D); D = mapobj.Compare<Car>(C, D); // Car D now has all the initialized properties of A,B,C } public class Mapper { public T Map<T>(T data) { var _result = (T)Activator.CreateInstance(typeof(T), new object[] { }); foreach (PropertyInfo propertyInfo in typeof(T).GetProperties()) { if (typeof(T).GetProperty(propertyInfo.Name) != null) typeof(T) .GetProperty(propertyInfo.Name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public) .SetValue(_result, propertyInfo.GetValue(data)); } return _result; } public T Compare<T>(T data, T data2) { var _result = (T)Activator.CreateInstance(typeof(T), new object[] { }); foreach (PropertyInfo propertyInfo in typeof(T).GetProperties()) { if (typeof(T).GetProperty(propertyInfo.Name) != null) { bool isnullvalue = false; DateTime zerodate = new DateTime(); switch (propertyInfo.PropertyType.Name) { case "String": if ((string)propertyInfo.GetValue(data) != null && (string)propertyInfo.GetValue(data2) == null) isnullvalue = true; break; case "Int32": if ((Int32)propertyInfo.GetValue(data) != 0 && (Int32)propertyInfo.GetValue(data2) == 0) isnullvalue = true; break; case "Int16": if ((Int16)propertyInfo.GetValue(data) != 0 && (Int16)propertyInfo.GetValue(data2) == 0) isnullvalue = true; break; case "Byte": if ((Byte)propertyInfo.GetValue(data) != 0 && (Byte)propertyInfo.GetValue(data2) == 0) isnullvalue = true; break; case "Double": if ((Double)propertyInfo.GetValue(data) != 0 && (Double)propertyInfo.GetValue(data2) == 0) isnullvalue = true; break; case "DateTime": // DateTime.Compare(date1, date2) DateTime time1 = (DateTime)propertyInfo.GetValue(data); DateTime time2 = (DateTime)propertyInfo.GetValue(data2); if (DateTime.Compare(time1, zerodate) != 0 && DateTime.Compare(time2, zerodate) == 0) isnullvalue = true; break; default: Console.WriteLine("No handler for type {} found"); Console.ReadLine(); Environment.Exit(-1); break; } if (isnullvalue) { typeof(T).GetProperty(propertyInfo.Name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public) .SetValue(_result, propertyInfo.GetValue(data)); } else { typeof(T).GetProperty(propertyInfo.Name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public) .SetValue(_result, propertyInfo.GetValue(data2)); } } } return _result; } } public class Car { public string Make { get; set; } public int Year { get; set; } public short ShortValue { get; set; } public byte ByteValue { get; set; } public DateTime CreateDate { get; set; } public double MSRP { get; set; } } }
}
毫无疑问,您可以在这里使用基于反射的解决方案,但您可能不需要它。如果你提前知道合并的类型,你可以写一个非常简单的映射函数来处理这个。
例如,给定一个简单的 class...
class MyClassToMap
{
public string MyString { get; set; }
public int MyInt { get; set; }
}
你可以写一个简单的方法...
MyClassToMap Map(params MyClassToMap[] toMap)
{
var mapped = new MyClassToMap();
foreach (var m in toMap)
{
// 'default' is shorthand for a type's uninitalized value. In the case of
// string, it resolves to "null", and in the case of int, it resolves to 0.
// You could also use the literal values here, if you prefer.
// Note that for C# versions < 7.1, you must specify the type--eg "default(string)".
// See: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/default
if (m.MyString != default) mapped.MyString = m.MyString;
if (m.MyInt != default) mapped.MyInt = m.MyInt;
}
return mapped;
}
然后这样称呼它...
var a = new MyClassToMap { MyString = "foo", MyInt = 0 };
var b = new MyClassToMap { MyString = "bar", MyInt = 100 };
var c = new MyClassToMap { MyString = null, MyInt = 0 };
var mapped = Map(a, b, c);
Console.WriteLine($"MyString = {mapped.MyString}, MyInt = {mapped.MyInt}");
// prints: { MyString = "bar", MyInt = 100 };
您可以为此目的使用反射。例如,请检查以下代码(添加到解释方法的方法中的内联注释)
public T AssignProperties<T>(params T[] sources)
{
// Types of properties that has to be copied
var filteredPropertyTypes = new []{typeof(int),typeof(string),typeof(short),typeof(byte),typeof(DateTime),typeof(double)};
// Create Default Instance of T
var newInstance = (T)Activator.CreateInstance(typeof(T));
// Iterate through each property
foreach(var property in typeof(T).GetProperties().Where(x=>filteredPropertyTypes.Contains(x.PropertyType)))
{
// Get the default Value of the Type and get the first instance in sources, which has a non-default value for the property
var defaultValue = property.PropertyType.IsValueType ? Convert.ChangeType(Activator.CreateInstance(property.PropertyType),property.PropertyType) : null;
if(sources.Any(x=>!Convert.ChangeType(property.GetValue(x),property.PropertyType).Equals(defaultValue)))
{
var newValue = property.GetValue(sources.First(x=>!Convert.ChangeType(property.GetValue(x),property.PropertyType).Equals(defaultValue)));
property.SetValue(newInstance,newValue);
}
}
return newInstance;
}
您现在可以将该方法用于 N 个实例(A、B、C...),实例按需要处理的顺序传递。
var newInstance = AssignProperties(instanceA,instanceB,instanceC);