I&QUOT哪有;深深"使用通用扩展方法克隆第三方类的属性?哪有、第三方、属性、方法

2023-09-03 03:09:11 作者:想住进你心里

借助 ICloneable .NET framework的接口通常提供了一种方法来支持的实例克隆一类。

The ICloneable interface of the .NET framework usually provides a way to support cloning of an instance of a class.

但是,如果我有多个第三方类,然后不想关心每个属性的独立,我怎么能复制这些类有效的对象? (这些类的源$ C ​​$ c是不可用)。 是否有使用仿制药的方式并扩展方法的?

But if I have multiple 3rd party classes, and don't want to care about each of the properties individually, how can I clone objects of those classes efficiently? (The source code of those classes is not available). Is there a way using generics and extension methods?

我需要的是创造,包括所有的属性和(子)对象。

What I need is a deep clone which creates an exact copy including all the properties and (child) objects.

推荐答案

随着通用扩展方法(加的反射),你可以做到这一点在C#这样的:

With a generic extension method (plus reflection) you can do it this way in C#:

public static class Extension
{
    public static T CreateCopy<T>(this T src)
        where T: new()
    {
        if (src == null) return default(T); // just return null
        T tgt = new T(); // create new instance
        // then copy all properties
        foreach (var pS in src.GetType().GetProperties())
        {
            foreach (var pT in tgt.GetType().GetProperties())
            {
                if (pT.Name != pS.Name) continue;
                (pT.GetSetMethod()).Invoke(tgt, new object[] { 
                    pS.GetGetMethod().Invoke(src, null) });
            }
        };
        return tgt;
    } // method
} // class

这是非常强大的,因为现在你可以克隆的每一个对象,不只是从你写的类的对象,而是来自各阶层的包括的系统类 .NET框架。并且由于反射,你不需要知道它的属性,自动复制它们。

This is very powerful, because now you can clone every object, not just objects from the classes you have written, but from all classes including the system classes of the .NET Framework. And thanks to reflection, you don't need to know its properties, they are copied automatically.

要使用的方法 CreateCopy(),假设你有一个客户类和订单类,您需要创建副本(而不是引用),但新的ID。然后,你可以做到以下几点:

To use the method CreateCopy(), let's say you have a Customer class and a Order class, and you need to create copies (not references) but with new IDs. Then you can do the following:

Order CopyOrderWithNewPK(Order item)
{
    Order newItem = item.CreateCopy(); // use ext. method to copy properties
    newItem.OrderId = new Guid(); // create new primary key for the item
    return newItem;
}

这并不奇怪,因为Customer类会看起来是一样的:

Not surprisingly, for the Customer class it would look the same:

Customer CopyCustomerWithNewPK(Customer item)
{
    Customer newItem = item.CreateCopy(); // use ext. method to copy properties
    newItem.CustomerId = new Guid(); // create new primary key for the item
    return newItem;
}

注意的示例类中定义的所有属性的值将自动复制。你甚至可以克隆一个第三方组件的对象,如果你没有自己的源$ C ​​$ C。权衡是,该反思的方法比较慢。

Note that the values of all properties defined within the example classes are copied automatically. You can even clone an object of a 3rd party assembly if you don't own the source code. The trade-off is, that the reflection approach is slower.

更新:

我找到了另一种方式来做到这一点,灵感来自这个问题(它不需要使用反射) 。一个优点是,它甚至是能够克隆实体框架的对象(例如附加和重新附加实体对象到一个不同的数据上下文):

I've found another way to do it, inspired by this question (it does not need to use reflection). One advantage is that it even is able to clone the objects of the Entity Framework (for example to attach and re-attach entity objects to a different data context):

public class Cloner<T>
{
    readonly DataContractSerializer _serializer 
            = new DataContractSerializer(typeof(T));

    /// <summary>
    /// Clone an object graph
    /// </summary>
    /// <param name="graph"></param>
    /// <returns></returns>
    public T Clone(T graph)
    {
        MemoryStream stream = new MemoryStream();
        _serializer.WriteObject(stream, graph);
        stream.Seek(0, SeekOrigin.Begin);
        return (T)_serializer.ReadObject(stream);
    }
}

为了不打破上面的例子中,您可以更改扩展方法 CreateCopy 如下:

In order to not break the examples above, you can change the extension method CreateCopy as follows:

public static class Extension
{   
    public static T CreateCopy<T>(this T src)
        where T: new()
    {
            return (new Cloner<T>()).Clone(src);
    }   
}   
 
精彩推荐
图片推荐