Reputation: 38667
I have an Item and a subclass AdvancedItem (all made of value-types if that matters):
public Item
{
public string A;
public bool B;
public char C;
...// 20 fields
}
public AdvancedItem : Item
{
public string Z;
}
It's easy to simply create an Item or an AdvancedItem independently:
var item = new Item { A = "aa", B = true, C = 'c', ... };
var aItem = new AdvancedItem { A = "aa", B = true, C = 'c', ..., Z = "zz" };
Now, I just want to turn an Item into an AdvancedItem by providing it the string Z separately. In order to achieve that I was thinking of using a constructor.
Attempt A:
// annoying, we are not using the inheritance of AdvancedItem:Item
// so we will need to edit this whenever we change the class Item
public AdvancedItem(Item item, string z)
{
A = item.A;
B = item.B;
...;//many lines
Z = z;
}
Attempt B:
// to use inheritance it seems I need another constructor to duplicate itself
public Item(Item item)
{
A = item.A;
B = item.B;
...;//many lines
}
public AdvancedItem(Item item, string z) : base(Item)
{
Z = z;
}
Is there any way to improve this second attempt to avoid writing many lines of X = item.X
? Maybe a solution to auto-clone or auto-duplicate a class with itself where public Item(Item item)
would be wrote in one line?
Upvotes: 7
Views: 3716
Reputation: 1928
Given that what you really want to do is serialize different parts of the object for different services, I would use Composition instead of Inheritance.
So you would have:
public Item
{
public string A;
public bool B;
public char C;
...// 20 fields
}
public AdvancedItem
{
[DataMember]
public Item baseItem;
[DataMember]
public string Z;
}
public Item(Item item)
{
A = item.A;
B = item.B;
...;//many lines
}
public AdvancedItem(Item item, string z)
{
baseItem = item; // or item.Clone();
Z = z;
}
Upvotes: 0
Reputation: 27085
Consider using AutoMapper to copy properties between objects.
This would allow the following:
Item a = new Item { A = 3, B = 'a', .... };
AdvancedItem advanced= Mapper.Map<AdvancedItem>(a);
string z = "Hello World";
advanced.Z = z;
Update
If you do not want to use AutoMapper
you can use Reflection
or better, Expressions
. However this will make your code a bit more complex
Consider these two types:
class Item
{
public int A, B, C;
public string D, E, F;
private int privateInt;
public Item(int valueOfPrivateField)
{
privateInt = valueOfPrivateField;
}
}
class AdvancedItem : Item
{
public string G;
public AdvancedItem(int valueOfPrivateField) : base(valueOfPrivateField)
{
}
}
We can define a method that creates a field-wise copy expression. Since you mention that all your fields are value types we can just copy each field one by one to the other object:
private static void MapFields<T>(T target, T source)
{
Type type = typeof (T);
if (!Mappers.ContainsKey(type))
{
//build expression to copy fields from source to target;
var targetParam = Expression.Parameter(typeof(object));
var targetCasted = Expression.TypeAs(targetParam, typeof(T));
var sourceParam = Expression.Parameter(typeof(object));
var sourceCasted = Expression.TypeAs(sourceParam, typeof(T));
var setters = new List<Expression>();
//get all non-readonly fields
foreach (var fieldInfo in typeof(T).GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).Where(f => !f.IsInitOnly))
{
Expression targetField = Expression.Field(targetCasted, fieldInfo);
Expression sourceField = Expression.Field(sourceCasted, fieldInfo);
setters.Add(Expression.Assign(targetField, sourceField));
}
Expression block = Expression.Block(setters);
var mapperFunction = Expression.Lambda<Action<object, object>>(block, targetParam,
sourceParam).Compile();
Mappers[type] = mapperFunction;
}
Mappers[type](target, source);
}
private static readonly Dictionary<Type, Action<object, object>> Mappers =
new Dictionary<Type, Action<object, object>>();
This caches functions that map all fields from the source to the target object, and should have close to the same performance as manually writing this.A = A, this.B = B
etc.
Calling the method:
static void Main(string[] args)
{
var item = new Item(56) {A = 5, B = 6};
var advanced = new AdvancedItem(0);
MapFields(advanced, item);
int a = advanced.A; //5
int b = advanced.B; //6;
//note that advanced.privateInt == 56!
}
Please note that this code is more complex and less reliable than AutoMapper and is not recommended or ready for production systems.
Upvotes: 6
Reputation: 109567
One object-oriented way to implement this kind of thing for class hierarchies is to introduce a protected copy constructor in the base class (although it still requires you to write all the assignments):
public class Item
{
protected Item(Item other)
{
this.A = other.A;
this.B = other.B;
this.C = other.C;
}
public string A;
public bool B;
public char C;
// 20 fields
}
Then you would call that from the derived class like so:
public class AdvancedItem : Item
{
public AdvancedItem(Item item, string z): base(item)
{
Z = z;
}
public string Z;
}
Note that this approach does NOT prevent you from having to write all the assignment lines, but you only need to write them once, and it does mean that you now have a protected copy constructor available, which can be very useful. Also, the assignments are now all in the base class where they belong.
This approach is extendable to further derived classes. You can introduce a protected copy constructor to AdvancedItem
written in terms of the public constructor (to avoid duplicated code).
For example:
public class AdvancedItem : Item
{
protected AdvancedItem(AdvancedItem other): this(other, other.Z)
{
}
public AdvancedItem(Item item, string z): base(item)
{
Z = z;
}
public string Z;
}
public class EvenMoreAdvancedItem: AdvancedItem
{
public EvenMoreAdvancedItem(AdvancedItem advancedItem, double q): base(advancedItem)
{
Q = q;
}
public double Q;
}
Upvotes: 4
Reputation: 11721
Yes as suggested by Bas you can go with Automapper but along with it there is one another option is using Otis..I know Automapper is better than Otis but it can be considered as one option..
Check it out :-
http://code.google.com/p/otis-lib/
As socumented in official site :-
Otis is a .Net object transformation library, i.e. an object to object mapper.
It automatically generates converter assemblies or classes which convert instances of one type to instances of some other type. These transformation can be described in type metadata using attributes, or separately in an XML source (file, string, database)
Otis is intended to be used to solve some common design and implementation tasks, e.g. to ease the implementation of support for DTO classes (more here), or to convert business domain type instances to presentation layer instances, but more generally, it can be used anywhere where a transformation between different types is needed.
Otis removes the need to manually implement type converters.
Upvotes: 0
Reputation: 1832
I would do something like this. Use a static factory method with a private constructor, and have a copy constructor defined in the base class.
AdvancedItem
{
Public Static AdvancedItem FromItem(Item i, string z)
{
AdvancedItem item = new AdvancedItem(i);
item.Z = z;
return item;
}
private AdvancedItem(Item i) : Base(i) {}
}
Then usage is AdvancedItem i = AdvancedItem.FromItem(item, extraThing);
Upvotes: 0