通过合并其他三个具有优先级的对象来创建对象 (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);

Demo Code