0

Possible Duplicate:
Cloning objects in C#

What I want to do is copy the values in a class from one object to another. Shallow Copy is just fine. However, I do not want to lose the reference that object has to the list/array/ienumerable. Also, I don't want to want to do this either:

public static void EditEvent(EventModel editEvent)
{
    EventModel changingEvent = EventRepository.getEvent(editEvent.EventID);
    changingEvent.Subject = editEvent.Subject;
    changingEvent.EventDate = editEvent.EventDate;
    changingEvent.EventDesc = editEvent.EventDesc;
    changingEvent.DayCode = editEvent.DayCode;
}

But rather:

public static void EditEvent(EventModel editEvent)
{
    EventModel changingEvent = EventRepository.getEvent(editEvent.EventID);
    changingEvent.CopyFrom(editEvent);
    //or
    editEvent.CopyTo(changingEvent);
}
Community
  • 1
  • 1
dotnetN00b
  • 5,021
  • 13
  • 62
  • 95
  • 1
    What reference are you referring to? – Adam Robinson Apr 20 '12 at 20:21
  • The object is actually an object in a list. So I don't want to lose the reference to that. Which would happen if I did something like this: changingEvent = editEvent; – dotnetN00b Apr 20 '12 at 20:23
  • 1
    you probably want to look at https://github.com/AutoMapper/AutoMapper – Aaron Anodide Apr 20 '12 at 20:24
  • It's similar but not the same. I have two objects that are ALREADY declared and instantiated. I need to copy the values one to the other. Ex. NOT this: someObject obj2 = obj1.Copy(); – dotnetN00b Apr 20 '12 at 20:54
  • inceidentally I stopped looking at AutoMapper when I couldn't get it to do what you are doing... i assumed I just couldn't figure it out but you're saying it doesn't work that way - anyway I decided it would be easier to maintain if i rolled my own - see the code I posted for how I got it working for purposes - it probably can be done a bit cleaner but it works – Aaron Anodide Apr 20 '12 at 21:44
  • @AaronAnodide - I was addressing the people that were that this question is a duplicate. I wasn't saying that AutoMapper doesn't work. However, please don't delete your code. I wanna try it. – dotnetN00b Apr 20 '12 at 22:09
  • 1
    i voted to re-open because the duplicate question doesn't seem to cover the case of when the destination object already exists, such as if you retrive it from an object container and want it to assume the field values of a matching object from a different container. Let me know if I'm wrong about this... – Aaron Anodide Apr 21 '12 at 04:27
  • @AaronAnodide - You are absolutely correct. – dotnetN00b Apr 21 '12 at 13:56
  • i find it strange that I do not come across other examples of this question with our seemingly mutual variation on the theme... the simplest explanation might be that we're missing something... the context i'm working in is ETL - i've been teaching myself though – Aaron Anodide Apr 21 '12 at 19:18

3 Answers3

1

You can do it like this

public class MyClonableClass : ICloneable
{
    public object Clone()
    {
        return this.MemberwiseClone();
    }
}

If you prefer to use a strongly typed method, just forget the interface

public class MyClonableClass
{
    public MyClonableClass Clone()
    {
        return (MyClonableClass)this.MemberwiseClone();
    }
}

This does not copy the fields of one object to another, but creates a new object which is an exact duplicate of the original. It makes a shallow copy.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • I need the former (copy fields of one object to another). – dotnetN00b Apr 20 '12 at 20:50
  • @dotnetN00b Yes, when Olivier writes `MyClonableClass`, think `EventModel`. After you have added the above `Clone` method to your `EventModel` class, you can do: `changingEvent = editEvent.Clone()`. – Jeppe Stig Nielsen Apr 20 '12 at 22:18
  • 1
    OK, this will not work if you need to alter an already existing object (that have existing references to it) since `Clone` creates a new object. But maybe you can just move the reference (by assigning)? Otherwise, I think you will have to write your `CopyFrom` method yourself, in your class `EventModel`. – Jeppe Stig Nielsen Apr 20 '12 at 22:33
1

This is code I wrote today. It passes simple tests. I'm thinking you can use it to get started.

Here is the test:

    /// <summary>
    ///A test for MapProperties
    ///</summary>
    public void MapPropertiesTestHelper<S, T>(S source, T target1, bool failIfNotMatched)
    {
        Cake.Common.Mapper<S, T> target = new Cake.Common.Mapper<S, T>();
        target.MapProperties(source, target1, failIfNotMatched);
    }

    [TestMethod()]
    public void MapPropertiesTest()
    {
        var source = new Source {
            OneTwo = 10,
            ThreeFour = "foo"
        };

        var target = new Target {
            OneTwo = 10,
            ThreeFour = "bar"
        };

        MapPropertiesTestHelper<Source, Target>(source, target, true);

        Assert.AreEqual(source.OneTwo, target.OneTwo);
        Assert.AreEqual(source.ThreeFour, target.ThreeFour);
        Assert.AreEqual(source.five_six, target.FiveSix);
    }

    public class Source
    {
        public int OneTwo { get; set; }
        public string ThreeFour { get; set; }
        public bool five_six { get; set; }
    }

    public class Target
    {
        public int OneTwo { get; set; }
        public string ThreeFour { get; set; }
        public bool FiveSix { get; set; }
    }

And here is the code:

public class MapperItem
{
    public MapperItem(MemberInfo member, object o)
    {
        this.Member = member;
        this.Object = o;
    }

    public MemberInfo Member 
    { 
        get; 
        set; 
    }

    public object Object
    {
        get;
        set;
    }

    public Type Type
    {
        get
        {
            return this.Member.UnderlyingType();
        }
    }

    public object Value
    {
        get
        {
            if (this.Member is PropertyInfo)
            {
                return (this.Member as PropertyInfo).GetValue(this.Object, null);
            }
            else if (this.Member is FieldInfo)
            {
                return (this.Member as FieldInfo).GetValue(this.Object);
            }
            else
            {
                throw new Exception("sourceMember must be either PropertyInfo or FieldInfo");
            }
        }
    }

    public object Convert(Type targetType)
    {
        object converted = null;

        if (this.Value == null)
        {
            return converted;
        }
        else if (targetType.IsAssignableFrom(this.Type))
        {
            converted = this.Value;
        }
        else
        {
            var conversionKey = Tuple.Create(this.Type, targetType);
            if (Conversions.ContainsKey(conversionKey))
            {
                converted = Conversions[conversionKey](this.Value);
            }
            else
            {
                throw new Exception(targetType.Name + " is not assignable from " + this.Type.Name);
            }
        }

        return converted;
    }

    public void Assign(object value)
    {
        if (this.Member is PropertyInfo)
        {
            (this.Member as PropertyInfo).SetValue(this.Object, value, null);
        }
        else if (this.Member is FieldInfo)
        {
            (this.Member as FieldInfo).SetValue(this.Object, value);
        }
        else
        {
            throw new Exception("destinationMember must be either PropertyInfo or FieldInfo");
        }
    }

    public static Dictionary<Tuple<Type, Type>, Func<object, object>> Conversions = new Dictionary<Tuple<Type, Type>, Func<object, object>>();
}

public class Mapper<S, T>
{
    private List<string> ignoreList = new List<string>();
    public List<string> IgnoreList
    {
        get { return ignoreList; }
        set { ignoreList = value; }
    }

    public void MapProperties(S source, T target, bool failIfNotMatched = true)
    {
        foreach (PropertyInfo property in source.GetType()
                                                      .GetProperties()
                                                      .Where(c => !IgnoreList.Contains(c.Name)))
        {
            try
            {
                var sourceField = new MapperItem(property, source);
                var targetField = new MapperItem(MatchToTarget(property), target);
                targetField.Assign(sourceField.Convert(targetField.Type));
            }
            catch (TargetNotMatchedException noMatch)
            {
                if (failIfNotMatched)
                {
                    throw noMatch;
                }
            }
        }
    }

    private MemberInfo MatchToTarget(MemberInfo member)
    {
        List<MemberInfo> members = new List<MemberInfo>();
        members.AddRange(typeof(T).GetProperties());
        members.AddRange(typeof(T).GetFields());

        var exactMatch = from c in members where c.Name == member.Name select c;

        if (exactMatch.FirstOrDefault() != null)
        {
            return exactMatch.First();
        }

        var sameAlphaChars = from c in members
                             where NormalizeName(c.Name) == NormalizeName(member.Name)
                             select c;

        if (sameAlphaChars.FirstOrDefault() != null)
        {
            return sameAlphaChars.First();
        }

        throw new TargetNotMatchedException(member, typeof(T));
    }

    private static string NormalizeName(string input)
    {
        string normalized = input.Replace("_", "").ToUpper();
        return normalized;
    }
}

public class TargetNotMatchedException : Exception
{
    public TargetNotMatchedException(MemberInfo member, Type type)
        : base("no match for member named " + member.Name + " with type named " + type.Name)
    {
        this.Member = member;
        this.Type = type;
    }
    public MemberInfo Member { get; set; }
    public Type Type { get; set; }
}

public static class ReflectionExtensions
{
    public static Type UnderlyingType(this MemberInfo member)
    {
        Type type;
        switch (member.MemberType)
        {
            case MemberTypes.Field:
                type = ((FieldInfo)member).FieldType;
                break;
            case MemberTypes.Property:
                type = ((PropertyInfo)member).PropertyType;
                break;
            case MemberTypes.Event:
                type = ((EventInfo)member).EventHandlerType;
                break;
            default:
                throw new ArgumentException("MemberInfo must be if type FieldInfo, PropertyInfo or EventInfo", "member");
        }
        return Nullable.GetUnderlyingType(type) ?? type;
    }
}
Aaron Anodide
  • 16,906
  • 15
  • 62
  • 121
  • I put the lastest version of this code on code review, http://codereview.stackexchange.com/questions/11231/is-my-mergeutility-good-enough-to-create-an-open-source-project-out-of – Aaron Anodide Apr 26 '12 at 17:52
0

You need Object.MemberwiseClone Method

Habib
  • 219,104
  • 29
  • 407
  • 436