从扩展方法

时间:2017-06-30 13:42:39

标签: c# clone

不确定我的白话是否正确所以请耐心等待。

我在插件中使用了一个公共接口,实现此接口的类我希望可以克隆,同时保持其声明类型和其中的所有属性。

我知道ICloneable并且我可以简单地将其实现到我的界面中,但我不想将实现接口的要求传递给插件开发人员并希望自己控制它。

另外值得注意的是,它需要重量轻,不需要很深。除了实现我的插件接口之外,我在设计时也不知道任何声明的类型,因此我需要将其转换为“未知”源类型。

public interface ImyInterface
{
    int commonProp {get;set;}
}

// This class and the properties therein are not known at design time
public myObj : ImyInterface
{
    int commonProp {get;set;}
    int uncommonProp {get;set;}
}

然后我需要从我的应用程序中调用类似的东西:

// This is how I generally "activate my plugins"
ImyInterface obj = (ImyInterface)Activator.CreateInstance(type);

// Then elsewhere I need to clone its current state.
var ClonedObj = obj.clone();

我试过this,但它要求我在设计时知道类型。

1 个答案:

答案 0 :(得分:2)

我建议您使用NuGet提供的DeepCloner。我认为这个库实现了你所需要的和扩展方法。它也是开源的,并在GitHub中托管,因此如果您想添加更多功能或只是想知道它是如何工作的,您可以检查代码。

从项目现场:

  

此外,无需指定克隆的对象类型。对象可以转换为inteface或作为抽象对象,你可以将int数组克隆为抽象数组或IEnumerable,甚至可以克隆null而不会出现任何错误。

我制作了这个样本来展示它的工作原理:

实现此接口的接口和类:

 interface IPluginInterface { }

    class Foo:IPluginInterface
    {
        public int SomeInt { get; set; }
        public string SomeString { get; set; }

        public Foo()
        {
            SomeInt = 42;
            SomeString = "SomeString";
        }

        public override string ToString() => $"SomeInt: {SomeInt}. SomeString: {SomeString}";
    }

然后在main中添加使用Force.DeepCloner; 和...

static void Main(string[] args)
        {

            IPluginInterface foo = new Foo();
            IPluginInterface fooWithActivator = (IPluginInterface) Activator.CreateInstance(typeof(Foo));
            Console.WriteLine(foo);
            var cloneOfFoo = foo.DeepClone();
            var cloneOfFooWithActivator = fooWithActivator.DeepClone();

            Console.WriteLine(cloneOfFoo);
            Console.WriteLine(cloneOfFoo == foo);
            Console.WriteLine(cloneOfFoo.GetType());

            Console.WriteLine(cloneOfFooWithActivator);
            Console.WriteLine(cloneOfFooWithActivator == foo);
            Console.WriteLine(cloneOfFooWithActivator.GetType());

            Console.ReadLine();
        }

输出:

enter image description here

编辑>>>>根据您对性能的担忧,我已经进行了一些测试,并找到了另一种更好的方法来实现您的目标。

更好的方法是使用 Reflection,Delegates和Jon Skeet 的混合方法调用 MemberwiseClone 方法。我们的想法是将methodinfo实例转换为委托,更多信息可以在Jon Skeet的this帖子中找到。

这是main():

 static void Main(string[] args)
        {
            const int howManyTimes = 10000000;
            IPluginInterface foo = new Foo(true);
            foo.ShallowCloneWithDeepClonerLibrary(howManyTimes);
            foo.ShallowCloneWithReflection(howManyTimes);
            ((Foo)foo).ShallowCloneWithMemberWiseClone(howManyTimes);
            foo.ShallowCloneWithDelegatesAndReflection(howManyTimes);
            Console.ReadLine();
        }

您可能已经注意到我们正在测试四种浅层克隆方法:

  • DeepCloner库
  • 直接公开MemberWiseClone。 (不适合您的情况)
  • 使用MethodInfo.Invoke,其中MethodInfo是MemberwiseClone
  • 使用委托

这是所有4种方法的代码(扩展方法):

public static void ShallowCloneWithDeepClonerLibrary(this object obj, int times)
    {
        Console.WriteLine($"Performing {times.ToString("##,###")} cloning operations with DeepCloner's ShallowClone method:");
        var sw = new Stopwatch();
        sw.Start();
        for (var i = 0; i < times - 1; i++) obj.ShallowClone();
        var clone = obj.ShallowClone();
        sw.Stop();
        Console.WriteLine($"Total milliseconds elapsed: {sw.ElapsedMilliseconds}");
        Console.WriteLine($"Are both the same: {obj == clone}");
        Console.WriteLine($"Cloned object: {Environment.NewLine}{clone}{Environment.NewLine}");
    }

    public static void ShallowCloneWithMemberWiseClone(this Foo obj, int times)
    {
        Console.WriteLine($"Performing {times.ToString("##,###")} cloning operations wiht MemberwiseClone:");
        var sw = new Stopwatch();
        sw.Start();
        for (var i = 0; i < times - 1; i++) obj.Clone();
        var clone = obj.Clone();
        sw.Stop();
        Console.WriteLine($"Total milliseconds: {sw.ElapsedMilliseconds}");
        Console.WriteLine($"Are both the same: {obj == clone}");
        Console.WriteLine($"Cloned object: {Environment.NewLine}{clone}{Environment.NewLine}");
    }

    public static void ShallowCloneWithDelegatesAndReflection(this object obj, int times)
    {
        Console.WriteLine(
            $"Performing {times.ToString("##,###")} cloning operations by encapsulating MemberwiseClone method info in a delegate:");
        var sw = new Stopwatch();
        sw.Start();
        var type = obj.GetType();
        var clone = Activator.CreateInstance(type);
        var memberWiseClone = type.GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic);
        var memberWiseCloneDelegate =
            (Func<object, object>)Delegate.CreateDelegate(typeof(Func<object, object>), memberWiseClone);
        for (var i = 0; i < times; i++) clone = memberWiseCloneDelegate(obj);
        sw.Stop();
        Console.WriteLine($"Total milliseconds: {sw.ElapsedMilliseconds}");
        Console.WriteLine($"Are both the same: {obj == clone}");
        Console.WriteLine($"Cloned object: {Environment.NewLine}{clone}{Environment.NewLine}");
    }

    public static void ShallowCloneWithReflection(this object obj, int times)
    {
        Console.WriteLine($"Performing {times.ToString("##,###")} cloning operations manually with reflection and MemberwiseClone:");
        var sw = new Stopwatch();
        sw.Start();
        var type = obj.GetType();
        var memberWiseClone = type.GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic);
        var clone = Activator.CreateInstance(type);
        for (var i = 0; i < times - 1; i++)
            clone = memberWiseClone.Invoke(obj, null);
        sw.Stop();
        Console.WriteLine($"Total milliseconds: {sw.ElapsedMilliseconds}{Environment.NewLine}");
        Console.WriteLine($"Are both the same: {obj == clone}");
        Console.WriteLine($"Cloned object: {Environment.NewLine}{clone}{Environment.NewLine}");
    }

结果以毫秒为单位进行10,000,000次克隆操作:

  • DeepCloner: 791
  • 直接使用MemberwiseClone: 463
  • 使用MethodInfo.Invoke的MemberwiseClone: 2000
  • 通过代表的MemberwiseClone: 465

所以,我们有一个胜利者!不幸的是,胜利者不适合你的情况,因为这意味着在课堂上暴露出MemberwiseClone。但是......我们有一个巨大的第二名!

这是输出:

enter image description here