2017-06-30 5 views
3

私の母国語が正しいかどうかわからないので、これで私に同行してください。拡張メソッドの派生型を維持しながらオブジェクトを複製する

私はパブリックインターフェイスをプラグインで使用しています。このインターフェイスを実装するクラスは、その宣言型とその中のすべてのプロパティを維持しながら、クローン可能でなければなりません。

私はICloneableを認識していますが、私はそれを自分のインターフェイスに実装することができますが、インターフェイスをプラグイン開発者に実装する必要はなく、自分でコントロールしたいと考えています。

また、軽量である必要があり、深くする必要がないことに注目する価値があります。私はまた、プラグインインタフェースを実装する以外に、宣言された型の設計時に何も知らないので、 "unknown"ソース型にキャストする必要があります。私はthisを試してみましたが、それは設計時に種類を知っている私を必要と

// 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(); 

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;} 
} 

その後、私は次のように自分のアプリケーションの気にいらないから呼び出す必要があります。

+2

、そして 'ICloneable'は*合理的*要件が以降渡すようです。一般的なケースでは、未知のオブジェクトの複製は非常に扱いにくいものです。 * 'GetType() 'で' FormatterServices.GetUninitializedObject'を介してコンストラクタをスキップし、 'private'や' readonly'でも強制的にインスタンスフィールドを設定することができます。)しかし、それは醜いです –

+0

@MarcGravell合理化のために、私はプラグイン開発者にクローニングメソッドを作成する責任を避けたかったのです。しかし、それ以外の方法がない場合は最後の手段です – Wobbles

+0

なぜ、クローンを作成する必要がありますか?クローンはどのような目的を果たすのでしょうか? –

答えて

1

NuGetから入手可能なDeepClonerを使用することをお勧めします。私はこのライブラリがちょうどあなたが必要とするものを実装していると思うと拡張メソッドとして。オープンソースであり、GitHubでホストされているので、機能を追加したい場合や、機能の仕組みを知りたい場合は、コードをチェックすることができます。

プロジェクトサイトから:

また、クローニングのためのオブジェクト・タイプを指定する必要はありません。オブジェクトをintefaceまたは抽象オブジェクトとしてキャストすることができます。抽象ArrayまたはIEnumerableとしてintの配列を複製できます。ヌルをエラーなく複製することもできます。

私はそれがどのように動作するかを示すために、このサンプルを作った:

インタフェースと、このインタフェースを実装するクラス:

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}"; 
    } 

その後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

EDIT >>>>パフォーマンスについてのあなたの懸念に基づいて、私はいくつかのテストを行い、また、達成するための別の、より良い方法を見つけるしていますあなたが欲しいもの。

良い方法アプローチは反射、参加者およびジョンスキートの混合物を使用してMemberwiseCloneメソッドを呼び出すことを含みます。アイデアは、methodinfoインスタンスをデリゲートに変換することです。詳細は、Jon Skeet氏の投稿thisにあります。ここで

は)(メインです:

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(); 
     } 

あなたは私たちが浅いクローニングのための4つのアプローチテストしている気付いているよう:直接MemberWiseCloneを公開

  • DeepClonerライブラリ
  • を。 (お使いの場合には適していません)METHODINFOは(拡張メソッドです)。これは、すべての4つのメソッドのコードであるデリゲート

を使用してMemberwiseClone

  • あるMethodInfo.Invokeを使用して
  • 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}"); 
        } 
    

    そして、それぞれの結果で1000万回のクローニング操作を行うミリ秒単位の結果:

    • ディpCloner:
    • MemberwiseClone直接:MethodInfo.Invokeと
    • MemberwiseClone:代理人を通じて
    • MemberwiseClone:

    だから、我々は勝者を持っています!残念ながら、クラス内のMemberwiseCloneを公開することを意味するため、勝者はあなたのケースには適していません。しかし...私たちはすごい2位!ここで

    が出力されます:あなたは* *それが複製可能であることを期待場合

    enter image description here

  • 関連する問題