Использование интерфейса для преобразования объекта из одного типа в другой?

Предположим, у меня есть два класса с одинаковым интерфейсом:

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

class SomeClass : ISomeInterface {}

class SomeOtherClass : ISomeInterface {}

Предположим, у меня есть экземпляр ISomeInterface, представляющий SomeClass. Есть ли простой способ скопировать это в новый экземпляр SomeOtherClass без копирования каждого члена вручную?

ОБНОВЛЕНИЕ: для протокола: я не пытаюсь привести экземпляр SomeClass к экземпляру SomeOtherClass. Я хотел бы сделать что-то вроде этого:

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

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

Я просто не хочу делать это для каждого вручную, так как у этих объектов много свойств.


person Jason Baker    schedule 19.11.2008    source источник
comment
Теперь существует структура для автоматических преобразований такого рода: automapper.org.   -  person Merlyn Morgan-Graham    schedule 21.02.2013


Ответы (8)


«Не могли бы вы привести мне пример того, как я могу это сделать (или, по крайней мере, указать мне правильные методы, которые следует использовать)? Кажется, я не могу найти их в MSDN», — Джейсон Бейкер.

Джейсон, что-то вроде следующего:

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

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

Я бы предложил написать инструмент, который бы выдавал код, необходимый для конструктора копирования, а не делать это во время выполнения, что было бы менее производительно.

person Winston Smith    schedule 19.11.2008
comment
Но прежде чем выполнять предварительную оптимизацию, попробуйте использовать отражение на лету. Если вы делаете что-то вроде стандартного бизнес-приложения, небольшое размышление о свойствах, вероятно, не имеет большого значения, а гибкость самокорректирующегося кода при добавлении свойств или изменении имен хороша. - person Chris Farmer; 20.11.2008

Вы можете создать неявные операторы в каждом классе, чтобы выполнить преобразование за вас:

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
}

и тогда будет работать SomeOtherClass soc = sc; и наоборот.

person Adam Lassek    schedule 19.11.2008

Разве смысл интерфейса не в том, чтобы этого не делать? Вы что-то делаете с конкретной реализацией SomeOtherClass? Вместо использования конкретной реализации используйте интерфейс, и не имеет значения, используете ли вы класс SomeClass или SomeOther.

Помимо этого, лучшее, что вы могли бы сделать, это написать какую-то вспомогательную функцию (вам все равно придется делать это вручную или заглянуть в отражение), которая копирует каждое свойство в интерфейсе, которое будет выглядеть так:

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

Тем не менее, моим первым побуждением было бы сказать, что держитесь подальше от реализации и вместо этого сконцентрируйтесь на своем интерфейсе, тогда не будет иметь значения, что лежит под ним.

person mendicant    schedule 19.11.2008

Отражение... перебирает каждое свойство и устанавливает его для соответствующего свойства другого объекта.

person Joel Martinez    schedule 19.11.2008
comment
Не могли бы вы привести мне пример того, как я могу это сделать (или, по крайней мере, указать мне на правильные методы, которые следует использовать)? Кажется, я не могу найти их в MSDN. :-/ - person Jason Baker; 20.11.2008
comment
И это не сработает, если свойства имеют разные типы... Ваш код должен будет сравнивать типы свойств, а также их имена... И тогда у вас также возникнет проблема работы со свойствами ссылочного типа - (глубокая копия или мелкая копия)? - person Charles Bretana; 20.11.2008

Ознакомьтесь с ответом Джо для решения Reflection.

Я предполагаю, что вы используете Visual Studio.

Знакомы ли вы с сочетаниями клавиш ctrl+shift+r и ctrl+shift+p? Если нет, ctrl+shift+r начинает/заканчивает запись макроса нажатия клавиши. ctrl+shift+p воспроизводит записанный макрос.

Что я сделал, когда установил много свойств, так это скопировал объявления свойств туда, где я хочу, чтобы они были установлены, и записал макрос для преобразования объявления в оператор set и перемещения курсора на следующую строку, затем я просто играйте, пока я не сделаю все установленные операторы.

person CrashCodes    schedule 19.11.2008
comment
Отличный совет по макросам! Не знал о них раньше и уже нашел им применение. Спасибо. - person Winston Smith; 20.11.2008
comment
В Delphi уже давно есть эта функция. Я был ооочень счастлив, когда узнал об этом. Я сталкивался с другими редакторами, в которых есть эта функция. - person CrashCodes; 21.11.2008
comment
Также проверьте, что произойдет, если вы наберете if, а затем нажмете Tab в VS. - person CrashCodes; 21.11.2008

Нет, чтобы прозрачно преобразовать (преобразовать) объект из одного типа в другой, базовый конкретный класс объекта, который у вас есть, должен наследоваться от класса, к которому вы пытаетесь его привести, даже если они оба реализуют один и тот же интерфейс.

Подумайте об этом, все два объекта должны иметь общее для реализации одного и того же интерфейса — это одно и то же подмножество сигнатур методов. У них может не быть (вероятно, нет) даже одинаковых свойств или полей данных.

person Charles Bretana    schedule 19.11.2008
comment
Может быть, я неправильно выразился. На самом деле я бы предпочел не бросать его. Было бы хорошо, если бы я копировал значения из одного экземпляра в другой. Я просто не хочу писать их вручную (у объектов много свойств). - person Jason Baker; 20.11.2008
comment
Что вы имеете в виду, я просто не хочу писать их вручную? Вы можете написать инструмент отражения, который проверяет все поля в одном классе и ищет 4 дубликата в другом, а затем, где есть совпадение, копирует значение... Но ему придется сравнивать типы данных и имена полей... беспорядочно ! - person Charles Bretana; 20.11.2008
comment
В противном случае вам придется кодировать это вручную, поле за полем или свойство за свойством... - person Charles Bretana; 20.11.2008
comment
Если свойств действительно много, напишите небольшой инструмент, который исследует объект с помощью отражения и выдает код, необходимый для конструктора копирования. - person Winston Smith; 20.11.2008

это не сработает?

class MyClass : ICloneable
{
public MyClass()
{

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

return mc;
}

Вам нужно только позвонить: MyClass.Clone().

person netadictos    schedule 19.11.2008
comment
Я думал об этом, но единственная проблема заключается в том, что базовый класс, от которого оба наследуются, имеет собственную реализацию Clone, которая возвращает объект того же типа. :-/ - person Jason Baker; 20.11.2008

Мне понравилось следующее, и он очень хорошо работает для преобразования одного объекта в другой с помощью неявного оператора:

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;
        }
    }
person Pankaj Awasthi    schedule 08.12.2010
comment
+1 - Отличный комментарий о способе сделать это с помощью операторов. Я думаю, что в этом случае лучше использовать отражение для копирования. - person Matthew M.; 22.12.2010