C# copy object?

I got some simple classes like below:

public class testClass
    {
        public int Id { get; set; }
        public string Property1 { get; set; }
        public string Property2 { get; set; }
        public string Property3 { get; set; }
    }
    public class testClassExtra
    {
        public int Id { get; set; }
        public string Property1 { get; set; }
        public string Property2 { get; set; }
        public string Property3 { get; set; }
        public string Property4 { get; set; }
        public string Property5 { get; set; }
    }

Open in new window


Let's say I got an instance of class testClassExtra:

testClassExtra test = new testClassExtra();
            test.Id = 1;
            test.Property1 = "Property1";
            test.Property2 = "Property2";
            test.Property3 = "Property3";
            test.Property4 = "Property4";
            test.Property5 = "Property5";

Open in new window


How can I have:

testClass test2 = new testClass();

Open in new window


then to copy the values from testClassExtra test?

I don't want to do something like this:

test2.Id = test.Id;
            test2.Property1 = test.Property1;
            test2.Property2 = test.Property2;
            test2.Property3 = test.Property3;

Open in new window


Is that possible?

Thank you.
trowaAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

trowaAuthor Commented:
I think I found a solution:

public static class MissingExtensions
    {
        public static void CopyTo(this object S, object T)
        {
            foreach (var pS in S.GetType().GetProperties())
            {
                foreach (var pT in T.GetType().GetProperties())
                {
                    if (pT.Name != pS.Name) continue;
                    (pT.GetSetMethod()).Invoke(T, new object[] { pS.GetGetMethod().Invoke(S, null) });
                }
            };
        }
    }

Open in new window

0
AndyAinscowFreelance programmer / ConsultantCommented:
A standard way of doing this is to use a copy constructor.

TestClass(TestClassExtra ob)
{
  Id = ob.Id;
  Property1 = ob.Property1;
}

so you would have something like this
TestClassExtra tce = new testClassExtra();
tce.Id = 1;
...

TestClass tc = new TestClass(tce);
0
trowaAuthor Commented:
Hi Andy,

I should mention this earlier, I can't use that "standard" method as I got a lot of classes which look similar. By doing so I need to copy all the properties of the classes to another one, that bring me 2 issues:
1. Time consuming to create those scripts
2. I'm afraid I would miss out some of it, or when there are additional properties being added, which I try to avoid.

Thank you
0
Amazon Web Services

Are you thinking about creating an Amazon Web Services account for your business? Not sure where to start? In this course you’ll get an overview of the history of AWS and take a tour of their user interface.

AndyAinscowFreelance programmer / ConsultantCommented:
OK.  Don't forget if it was suitable you could implement code like your copy routine in the copy constructor.

ps.  similar classes - would inheritance be useful if you don't use it at present?
0
sarabandeCommented:
you could implement code like your copy routine in the copy constructor.

would inheritance be useful if you don't use it at present?

if you would make TestClass baseclass of TestClassExtra, your requirements might be done easier:

    public class TestClass
    {
        public int Id { get; set; }
        public string Property1 { get; set; }
        public string Property2 { get; set; }
        public string Property3 { get; set; }

       // copy constructor
       public TestClass(TestClass toCopy)
       {
               id               = toCopy.id;
               Property1 = toCopy.Property1;
               Property2 = toCopy.Property2;
               Property3 = toCopy.Property3;
        }
   public class TestClassExtra : public TestClass
   {
        public string Property4 { get; set; }
        public string Property5 { get; set; }
   }

Open in new window


then you could do

  testClassExtra testEx = new testClassExtra();
  testEx.Id = 1;
  testEx.Property1 = "Property1";
  testEx.Property2 = "Property2";
  testEx.Property3 = "Property3";
  testEx.Property4 = "Property4";
  testEx.Property5 = "Property5";

   // cast testEx object to its baseclass object and use copy constructor of TestClass to assign the id and properties
  TestClass test = new TestClass((TestClass)testEx); 

Open in new window


Sara
0
trowaAuthor Commented:
Hi Sara,

Knowing I can do that by using similar method you have showing above, but the idea behind my intention is not to repetitively write the whole similar codes.

Thank you.
0
sarabandeCommented:
my intention is not to repetitively write the whole similar codes.

unfortunately c# has no generic copy constructor or assignment operator (like c++).

but i found sample code how to write a generic clone function which could be used to copy all members into a new copy object:

public static class Extension
{
    public static T CreateCopy<T>(this T src)
        where T: new()
    {
        if (src == null) return default(T); // just return null
        T tgt = new T(); // create new instance
        // then copy all properties
        foreach (var pS in src.GetType().GetProperties())
        {
            foreach (var pT in tgt.GetType().GetProperties())
            {
                if (pT.Name != pS.Name) continue;
                (pT.GetSetMethod()).Invoke(tgt, new object[] { 
                    pS.GetGetMethod().Invoke(src, null) });
            }
        };
        return tgt;
    } // method
} // class

Open in new window


this template function was used like that

Customer CopyCustomerWithNewPK(Customer item)
{
    Customer newItem = item.CreateCopy(); // use ext. method to copy properties
    newItem.CustomerId = new Guid(); // create new primary key for the item
    return newItem;
}

Open in new window


note, the above would generate a new id for the customer (PK). that probably has to be considered  with your approach as well.

you may find this and more samples by searching for 'c# automatic copy constructor'.

Sara
0
it_saigeDeveloperCommented:
A better CopyTo method would also check to see if the target property can be written to (you can also drop one of the loops by just getting the properties from the target that exist in the source); e.g. -
static class Extensions
{
    public static T CopyTo<S, T>(this S source) where T : class, new()
    {
        T target = Activator.CreateInstance<T>();
        foreach (var s_prop in typeof(S).GetProperties(BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static))
        {
            var t_prop = typeof(T).GetProperty(s_prop.Name);
            if (t_prop != null && t_prop.CanWrite)
            {
                try
                {
                    t_prop.SetValue(target, s_prop.GetValue(source, null));
                }
                catch (Exception)
                {
                    // There was a problem setting the value of the property 
                    // in the target object.  Log it, handle it or both.
                }
            }
        }
        return target;
    }
}

Open in new window

Proof of concept -
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace EE_Q29072166
{
    class Program
    {
        static readonly List<testClassExtra> extraClasses = (from i in Enumerable.Range(0, 10)
                                                             select new testClassExtra
                                                             {
                                                                 ID = i,
                                                                 Property1 = $"testClassExtra{i}_Property1",
                                                                 Property2 = $"testClassExtra{i}_Property2",
                                                                 Property3 = $"testClassExtra{i}_Property3",
                                                                 Property4 = $"testClassExtra{i}_Property4",
                                                                 Property5 = $"testClassExtra{i}_Property5"
                                                             }).ToList();
        static void Main(string[] args)
        {
            List<testClass> classes = new List<testClass>(from x in extraClasses select x.CopyTo<testClassExtra, testClass>());
            foreach (var @class in classes)
                Console.WriteLine(@class);
            Console.ReadLine();
        }
    }

    class testClass
    {
        public int ID { get; set; }
        public string Property1 { get; set; }
        public string Property2 { get; set; }
        public string Property3 { get; set; }

        public override string ToString()
        {
            return $"Type: {GetType()}, ID: {ID}, Property1: {Property1}, Property2: {Property2}, Property3: {Property3}";
        }
    }

    class testClassExtra
    {
        public int ID { get; set; }
        public string Property1 { get; set; }
        public string Property2 { get; set; }
        public string Property3 { get; set; }
        public string Property4 { get; set; }
        public string Property5 { get; set; }
    }

    static class Extensions
    {
        public static T CopyTo<S, T>(this S source) where T : class, new()
        {
            T target = Activator.CreateInstance<T>();
            foreach (var s_prop in typeof(S).GetProperties(BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static))
            {
                var t_prop = typeof(T).GetProperty(s_prop.Name);
                if (t_prop != null && t_prop.CanWrite)
                {
                    try
                    {
                        t_prop.SetValue(target, s_prop.GetValue(source, null));
                    }
                    catch (Exception)
                    {
                        // There was a problem setting the value of the property 
                        // in the target object.  Log it, handle it or both.
                    }
                }
            }
            return target;
        }
    }
}

Open in new window

Produces the following output -Capture.PNG
-saige-
1

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
it_saigeDeveloperCommented:
You could also get rid of the need of activating a new instance by passing in an instance; e.g. -
static class Extensions
{
    public static T CopyTo<S, T>(this S source, T target) where T : class, new()
    {
        foreach (var s_prop in typeof(S).GetProperties(BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static))
        {
            var t_prop = typeof(T).GetProperty(s_prop.Name);
            if (t_prop != null && t_prop.CanWrite)
            {
                try
                {
                    t_prop.SetValue(target, s_prop.GetValue(source, null));
                }
                catch (Exception)
                {
                    // There was a problem setting the value of the property 
                    // in the target object.  Log it, handle it or both.
                }
            }
        }
        return target;
    }
}

Open in new window

This will change the call to this:
var x = new testClassExtra();
var y = x.CopyTo(new testClass())

Open in new window

The above example would be changed like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace EE_Q29072166
{
    class Program
    {
        static readonly List<testClassExtra> extraClasses = (from i in Enumerable.Range(0, 10)
                                                             select new testClassExtra
                                                             {
                                                                 ID = i,
                                                                 Property1 = $"testClassExtra{i}_Property1",
                                                                 Property2 = $"testClassExtra{i}_Property2",
                                                                 Property3 = $"testClassExtra{i}_Property3",
                                                                 Property4 = $"testClassExtra{i}_Property4",
                                                                 Property5 = $"testClassExtra{i}_Property5"
                                                             }).ToList();
        static void Main(string[] args)
        {
            List<testClass> classes = new List<testClass>(from x in extraClasses select x.CopyTo(new testClass()));
            foreach (var @class in classes)
                Console.WriteLine(@class);
            Console.ReadLine();
        }
    }

    class testClass
    {
        public int ID { get; set; }
        public string Property1 { get; set; }
        public string Property2 { get; set; }
        public string Property3 { get; set; }

        public override string ToString()
        {
            return $"Type: {GetType()}, ID: {ID}, Property1: {Property1}, Property2: {Property2}, Property3: {Property3}";
        }
    }

    class testClassExtra
    {
        public int ID { get; set; }
        public string Property1 { get; set; }
        public string Property2 { get; set; }
        public string Property3 { get; set; }
        public string Property4 { get; set; }
        public string Property5 { get; set; }
    }

    static class Extensions
    {
        public static T CopyTo<S, T>(this S source, T target) where T : class, new()
        {
            foreach (var s_prop in typeof(S).GetProperties(BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static))
            {
                var t_prop = typeof(T).GetProperty(s_prop.Name);
                if (t_prop != null && t_prop.CanWrite)
                {
                    try
                    {
                        t_prop.SetValue(target, s_prop.GetValue(source, null));
                    }
                    catch (Exception)
                    {
                        // There was a problem setting the value of the property 
                        // in the target object.  Log it, handle it or both.
                    }
                }
            }
            return target;
        }
    }
}

Open in new window

And would produce the same output as shown above.

-saige-
1
trowaAuthor Commented:
Hi -saige-

I was trying your first suggestion in ID: 42396126:

static class Extensions
    {
        public static T CopyTo<S, T>(this S source) where T : class, new()
        {
            T target = Activator.CreateInstance<T>();
            foreach (var s_prop in typeof(S).GetProperties(System.Reflection.BindingFlags.IgnoreCase | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static))
            {
                var t_prop = typeof(T).GetProperty(s_prop.Name);
                if (t_prop != null && t_prop.CanWrite)
                {
                    try
                    {
                        t_prop.SetValue(target, s_prop.GetValue(source, null));
                    }
                    catch (Exception)
                    {
                        // There was a problem setting the value of the property 
                        // in the target object.  Log it, handle it or both.
                    }
                }
            }
            return target;
        }
    }

Open in new window


but I getting error below.

Error	1	No overload for method 'SetValue' takes 2 arguments

Open in new window


I'm using VS 2010, what modification need to be made to compatible in this environment?

Thank you.
0
trowaAuthor Commented:
Hi -saige-

I have solved my issue above by passing a 3rd parameter with null value.

But I must say that you're very knowledgeable in .NET programming!! Are you a lecturer that teaching .NET programming or someone whom got lots of hands-on experiences in .NET programming?

Thank you
0
trowaAuthor Commented:
By the way, may I know what does this line of code means?

public static T CopyTo<S, T>(this S source, T target) where T : class, new()

Open in new window


Thank you
0
it_saigeDeveloperCommented:
Just a lot of hands on experience.  With regards to the meaning of the line of code.  It is a generic method signature. In order of appearance, the line contains the following: an access modifier, a static designator, a Type specification which represents the return type, a method name, a generic type parameter list, a method parameter list and a generic type constraint.

For the most part, a lot of this you a probably already familiar with, so I'll focus on the generics.  Imagine that you have a few methods like this;
static string MyFunction(short y, string x) { return y + x; }
static string MyFunction(int y, string x) { return y + x; }
static string MyFunction(long y, string x) { return y + x; }
static string MyFunction(float y, string x) { return y + x; }
static string MyFunction(decimal y, string x) { return y + x; }
static string MyFunction(double y, string x) { return y + x; }

Open in new window

Notice a pattern?  This is where generics come in.  You can take your pattern and condense it into:
static string MyFunction<T>(T y, string x) { return y + x; }

Open in new window

The only difference between the above example and my implementation is that I used a second generic and said that I would return a type that is defined by the second generic parameter, I could have easily specified the first parameter as the return type.  I also used a constraint to say only allow types of T where T is a class object and T has a public parameterless constructor.

-saige-
1
trowaAuthor Commented:
Thanks for the assistance
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.