开发者

Copying objects to 'this' object in C#

I have a certain hirerchy of classes that needs the capeability to copy all public properties from one object to another.

Each class has a certain set of public properties that might differ from any other class.

Example:

class Base
{
  // Common properties/methods...
  public void Copy<T>(T data) where T : Base
  {
     // ...
  }
}

class D1 : Base
{
  public int ID
  {
    get;
    set;
  }
}

class D2 : Base
{
  public string Name
  {
    get;
    set;
  }
}

Through googling I have read about those methods:

  • Using reflection
  • Generating IL code
  • Serialization

All of them are either very complex or very slow or sometimes both.

Am I missing something? Is there an开发者_运维问答y other way to access the raw this pointer?

EDIT:

I will clerify.

T is of the type of the calling class. For example if it was called by D1 T will always be D1.

The reason for the generic is that I can't really know what T is.

Am I missing something?

Should I just use Base data as the parameter?


What you're missing is that you're asking the compiler to know that T might be one of the types D1 and D2 when all you've said is that T is a Base. How could it possible know what properties or even type your object is as that information is only known at runtime. Even if you could go foreach (PropertyInfo in this.Properties) it's going to find out the name of those properties at runtime so be just as slow as Reflection because how else can it? (it is reflection, just prettier syntax). It can't know what properties are common until it knows what types it's dealing with and you've said "i'm not telling you until runtime" so the answer is "well I'll have to look at runtime" i.e. reflection.

Secondly, just because D1 and D2 might both have a property named Size doesn't mean they are the same property (unless that property is present in a common ancestor).

For example,

  • ArtilleryAmmo.Shell and PecanNut.Shell.
  • AcmeCorp.Staff and GandolfTheWizard.Staff
  • California.State and MyBrokenEngine.State
  • LoudSpeaker.Volume and MassiveCrater.Volume
  • Cave.Bats and BasketballGame.Bats

etc. etc.


You could work around this using an architectural change and use a 'PropertyBag' to store each class' properties.

A PropertyBag is essentially a Dictionary<string, object> where you can give a piece of data a name and add it to the bag. The disadvantage is that everything gets cast to object, so it isn't very type safe plus there's lots of boxing/unboxing, plus the strings as names don't get checked at compile time, so typos are a constant threat.

When you define a property on the class, you store/retrieve the item from the class' propertybag:

public int MyProperty
{
    get
    {
        return (int)_propertyBag["MyProperty"];
    }
    set
    {
        if(_propertyBag.Keys.Contains("MyProperty"))
        {
          _propertyBag["MyProperty"] = value;
        }
        else
        {
          _propertyBag.Add("MyProperty", value);
        }
    }
}

So now to aggregate all the properties of the derived classes, you can expose their 'raw' PropertyBag and iterate through it.

Like I said before, the PropertyBags aren't type-safe, so it you have two classes in the hierarchy with the same property name but different type then you're getting into trouble.

EDIT: If you're concerned with performance, you're going to have to implement this multiple ways and perf test the different implementations -- I can't honestly say if a PropertyBag will actually be faster than using reflection.


The Copy method in the Base class only has access to the properties that are defined in the Base class. You can copy these properties.

But you cannot copy the properties from the subclasses without using something like reflection. But even with reflection you need some kind of knowledge about the mapping of the properties between different subclasses, like copying the ID property to Name.

So you'll need to write separate implementations for each (allowed) subclass conversion.

public interface IBaseCopier<TFrom, TTo> where TFrom : Base, TTo : Base
{
  void Copy(TFrom from, TTo to);
}

public class D1ToD2Copier : IBaseCopier<D1, D2>
{
  public void Copy(D1 from, D2 to)
  {
    // Copy properties from the D1 instance to the D2 instance.
  }
}

You can register all the ICopier<TFrom, TTo> implementations in a factory class. This class will look up the implementation of the copier, based on the type arguments. If there is no copier for a certain type combination, i.e. the conversion is not supported, the factory should throw an exception.

public class CopierFactory
{
  public ICopier<TFrom, TTo> Create<TFrom, TTo>() where TFrom : Base, TTo : Base
  {
    // Look up the ICopier implementation for the given types.
  }
}

Edit

You can use the MemberwiseClone method to create a copy of an object.

public class Base
{
  public static T Copy<T>(T data) where T : Base
  {
    return data.MemberwiseClone() as T;
  }
}

If you need more control over the cloning, you can implement the ICloneable interface.

Note: You should realize that you cannot clone a D1 instance into a D2 instance. That would be like cloning a sheep into a horse.


I think the copy method should be inherited by derived classes D1,D2 and their responsibility to copy their own properties to/from other types.


What I would do is create an extension method for the Base class like:

namespace ExtensionMethods
{
    public static class MyExtensions
    {
        public static int CopyTo<T>(this Base source, ref T dest)
        {
          // Use reflection to cycle public properties and if you find equally named ones, copy them.
        }
    }   
}

Then you could call it in your objects and like:

  source.CopyTo<ClassType>(ref this);

I didn't test, so not sure if it would work exactly like described. I did something similar to Cast DataRows into Entities in a big project I worked on.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜