Using an interface to convert an object from one type to another?
Asked Answered
C

10

9

Suppose I have two classes with the same interface:

interface ISomeInterface 
{
    int foo{get; set;}
    int bar{get; set;}
}

class SomeClass : ISomeInterface {}

class SomeOtherClass : ISomeInterface {}

Suppose I have an instance of ISomeInterface that represents a SomeClass. Is there an easy way to copy that into a new instance of SomeOtherClass without copying each member by hand?

UPDATE: For the record, I'm not trying to cast the instance of SomeClass into the instance of SomeOtherClass. What I'd like to do is something like this:

ISomeInterface sc = new SomeClass() as ISomeInterface;
SomeOtherClass soc = new SomeOtherClass();

soc.foo = sc.foo;
soc.bar = soc.bar;

I just don't want to have to do that for each by hand as these objects have lots of properties.

Coben answered 19/11, 2008 at 22:5 Comment(2)
There is a framework for automatically these sorts of conversions now: automapper.orgEjaculatory
Alternatives to AutoMapper: https://mcmap.net/q/264248/-alternatives-to-automapper-closedManicdepressive
A
3

"Would you be able to give me an example of how I can do that (or at least point me towards the right methods to be using)? I don't seem to be able to find them on MSDN" – Jason Baker

Jason, something like the following:

var props = typeof(Foo)
            .GetProperties(BindingFlags.Public | BindingFlags.Instance);

foreach (PropertyInfo p in props)
{
     // p.Name gives name of property
}

I'd suggest writing a tool to spit out the code you need for a copy constructor, as opposed to doing it reflectively at runtime - which would be less performant.

Alric answered 19/11, 2008 at 23:10 Comment(1)
But before you pre-optimize, try it with using on-the-fly reflection. If you're doing something like a standard line-of-business app, a little reflection on properties probably isn't a big deal, and the flexibility of having self-correcting code when props are added or names changed is nice.Daff
Y
9

You can create implicit operators in each class to do the conversion for you:

public class SomeClass
{
    public static implicit operator SomeOtherClass(SomeClass sc)
    {
        //replace with whatever conversion logic is necessary
        return new SomeOtherClass()
        {
            foo = sc.foo,
            bar = sc.bar
        }
    }

    public static implicit operator SomeClass(SomeOtherClass soc)
    {
        return new SomeClass()
        {
            foo = soc.foo,
            bar = soc.bar
        }
    }
    //rest of class here
}

and then SomeOtherClass soc = sc; and vice versa would work.

Yahoo answered 19/11, 2008 at 22:27 Comment(0)
W
6

Isn't the point of an interface to not have to do that? Are you doing something with the concrete implementation of SomeOtherClass? Instead of using the concrete implementation, use the interface and it shouldn't matter if you use SomeClass or SomeOther class.

Other than that, the best you could do is to write some sort of a helper function (you'd still have to do it manually, or look into reflection) that copies each Property on the Interface that would look like this:

   public ISomeInterface CopyValues(ISomeInterface fromSomeClass, ISomeInterface toSomeOtherClass)
   {
    //copy properties here
    return toSomeOtherClass;
    }

However, my first instinct would be to say stay away from the implementation and concentrate using your interface instead, then it won't matter what's lying underneath.

Wren answered 19/11, 2008 at 22:12 Comment(1)
it can be usefull when it comes to serialization and deserilization where you cannot use an interfaceCicada
D
4

Reflection ... loop through every property, and set it on the corresponding property on the other object.

Daddylonglegs answered 19/11, 2008 at 22:8 Comment(2)
Would you be able to give me an example of how I can do that (or at least point me towards the right methods to be using)? I don't seem to be able to find them on MSDN. :-/Coben
And this won't work if the propertys are of different types... TYour code will have to compare property types as well as their names... And then you also have the issue of dealing with reference type proprtys - (Deep copy or shallow copy)?Irrelievable
N
4

Check out Joe's answer for the Reflection solution.

I assume you are using Visual Studio.

Are you familiar with the ctrl+shift+r and ctrl+shift+p shortcuts? If not, ctrl+shift+r begins/ends recording a keystroke macro. ctrl+shift+p plays the recorded macro.

What I've done when I have set a lot of properties is to copy the property declarations to where I want them to be set and record a macro for mutating the declaration to a set statement and moving the cursor to the next line, then I just play it until I have all the set statements done.

Nerveracking answered 19/11, 2008 at 23:31 Comment(3)
Great tip on the macros! Wasn't aware of them before, and have found a use for it already. Thanks.Alric
Delphi has had this feature for a long time. I was sooo happy when I learned about it. I've encountered other editors that this feature.Nerveracking
Also check out what happens if you type "if" then hit tab in VS.Nerveracking
I
3

No, to transparently convert (cast) an object from one type to another, the underlying concrete class of the object you have, must inherit from the class the you are trying to cast it to, even if they both inplement the same interface.

Think about it, all two objects have to have in common to implement the same interface is the same subset of method signatures. They might not (probably do not) even have the same properties or data fields.

Irrelievable answered 19/11, 2008 at 22:10 Comment(4)
Maybe I misworded myself. I would actually prefer not to cast it. It would be fine by me to copy values from one instance to another. I just don't want to have to write those by hand (the objects have a lot of properties).Coben
What do you mean, "I just don't want to have to write those by hand"? You could write a reflection tool that examines all fields in one class and looks 4 dupes in the other, and then where there's a match, copy the value... But it would have to compare data types and field names.... messy!Irrelievable
Otherwise you will have to code this by hand, field by field or property by property...Irrelievable
If there are really a lot of properties then write a little tool that examines the object using reflection and spits out the code you need for a copy constructor.Alric
H
3

wouldn't this work?

class MyClass : ICloneable
{
public MyClass()
{

}
public object Clone() // ICloneable implementation
{
MyClass mc = this.MemberwiseClone() as MyClass;

return mc;
}

You only have to call: MyClass.Clone().

Hydrodynamics answered 19/11, 2008 at 22:13 Comment(1)
I thought about that, but the only problem is that the base class that both inherits from has their own implementation of Clone that returns an object of the same type. :-/Coben
A
3

"Would you be able to give me an example of how I can do that (or at least point me towards the right methods to be using)? I don't seem to be able to find them on MSDN" – Jason Baker

Jason, something like the following:

var props = typeof(Foo)
            .GetProperties(BindingFlags.Public | BindingFlags.Instance);

foreach (PropertyInfo p in props)
{
     // p.Name gives name of property
}

I'd suggest writing a tool to spit out the code you need for a copy constructor, as opposed to doing it reflectively at runtime - which would be less performant.

Alric answered 19/11, 2008 at 23:10 Comment(1)
But before you pre-optimize, try it with using on-the-fly reflection. If you're doing something like a standard line-of-business app, a little reflection on properties probably isn't a big deal, and the flexibility of having self-correcting code when props are added or names changed is nice.Daff
L
3
   ISomeInterface sc = new SomeClass() as ISomeInterface;
   SomeOtherClass soc = new SomeOtherClass();
   foreach (PropertyInfo info in typeof(ISomeInterface)
                                     .GetProperties(BindingFlags.Instance
                                                     |BindingFlags.Public))
   {
       info.SetValue(soc,info.GetValue(sc,null),null);
   }
Lacerta answered 19/11, 2008 at 23:53 Comment(0)
P
2

I did like the following and it works just very well to convert from one object into another using implicit operator:

class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("hello");
            ExecutionReport er = new ExecutionReport("ORDID1234",3000.43,DateTime.UtcNow);
            Order ord = new Order();
            ord = er;
            Console.WriteLine("Transferred values are : " + er.OrderId + "\t" + ord.Amount.ToString() + "\t" + ord.TimeStamp.ToString() + "\t");
            Console.ReadLine();
        }
    }


    public  class Order
    {
        public string OrderId { get; set; }
        public double Amount { get; set; }
        public DateTime TimeStamp { get; set; }
        public static implicit operator ExecutionReport(Order ord)
        {
            return new ExecutionReport()
            {
                OrderId = ord.OrderId,
                Amount = ord.Amount,
                TimeStamp = ord.TimeStamp
            };
        }
        public static implicit operator Order(ExecutionReport er)
        {
            return new Order()
            {
                OrderId = er.OrderId,
                Amount = er.Amount,
                TimeStamp = er.TimeStamp
            };
        }

        public Order()
        { }
    }

    public  class ExecutionReport
    {
        public string OrderId { get; set; }
        public double Amount { get; set; }
        public DateTime TimeStamp { get; set; }
        public ExecutionReport() { }
        public ExecutionReport(string orderId,double amount, DateTime ts)
        {
            OrderId = orderId; Amount = amount; TimeStamp = ts;
        }
    }
Photometry answered 8/12, 2010 at 20:56 Comment(1)
+1 - Great comment for a way to do this using operators. I think in this instance better than using reflection to do the copying.Parabasis
C
0

Tvanfosson's answer can be extended using a static generic method defined in the interface:

public interface IPerson
{
    string Name { get; set; }

    static void CopyProperties<A, B>(A source, B dest) where A : IPerson where B : IPerson
    {
        foreach (var property in typeof(IPerson).GetProperties())
        {
            property.SetValue(dest, property.GetValue(source));
        }
    }
}

Classes implementing IPerson:

public class Worker : IPerson
{
    public string Name { get; set; }
}

public class Manager : IPerson
{
    public string Name { get; set; }
}

Usage:

var worker = new Worker { Name = "John" };
var manager = new Manager();

IPerson.CopyProperties(worker, manager);

Alternatively, you can use the new constraint to create an instance of the desired type given that it implements the interface:

 public interface IPerson
 {
     string Name { get; set; }
 
     static TDest ChangeType<TDest, TSource>(TSource source) where TSource : IPerson where TDest : IPerson, new()
     {    
         var instance = new TDest();
         foreach (var property in typeof(IPerson).GetProperties())
         {
             property.SetValue(instance, property.GetValue(source));
         }
         return instance;
     }
 }
 

Usage:

var worker = new Worker { Name = "John" };
var manager = IPerson.ChangeType<Manager, Worker>(worker);
Cicada answered 28/8, 2022 at 3:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.