views:

164

answers:

5

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 any 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?

+6  A: 

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.

Graphain
+1 - Excellent examples
Daniel May
+2  A: 

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.

Dr Herbie
+1  A: 

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

Arseny
This would create a lot of coupling between the subclasses.
Niels van der Rest
A: 

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.

Francisco Soto
This is a public method, I'm using the copying internally.
the_drow
Also, you cannot ref this since it's readonly.
the_drow
+1  A: 

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.

Niels van der Rest
Is it possible to just use a copy Ctor since I'm just copying from T to this of type T?
the_drow
You mean something like `public D1(D2 data) { }`, where the `D1` constructor will copy data from the `D2` instance?
Niels van der Rest
You guys still don't get it.The problem is that I can't do this = data; // typeof(data) == typeof(this), both inherit from base.So it will be something like D1(D1 data) {}The problem is, how do I assign data to this?
the_drow
The keyword `this` points to the instance on which a method is called, the `data` parameter will point to a different instance. You cannot assign a value to `this`. You will have to copy all the properties instead. I'll update my answer to explain cloning.
Niels van der Rest
I know MemberwiseClone. But how do I assign it to this. In C++ I would just do *this = data;
the_drow
You cannot assign to `this` in classes in C#. However, you *can* in structs, see [this question](http://stackoverflow.com/questions/69843/what-is-an-example-of-this-assignment-in-c).
Niels van der Rest
Hmm interesting. So maybe my Data should be in a struct?
the_drow
Correct me if I'm wrong, but I think you're trying to use C# like C++. So I would advise against using structs just for being able to assign to this. [Structs are different from classes](http://stackoverflow.com/questions/13049/whats-the-difference-between-struct-and-class-in-net/13275#13275). Wouldn't this code accomplish the same as assigning to `this` from within `D1`: `D1 data = new D1(); D1 data2 = data;`?
Niels van der Rest
@Niels van der Rest: It might. I just have to code things differently. Why is this unassignable in a class?
the_drow
That's how the language is designed. I couldn't find anything about the rationale behind this decision, perhaps someone else can clarify the matter?
Niels van der Rest