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

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

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;

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

10 ответов

Решение

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

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

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

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

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

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

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; и наоборот будет работать.

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

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

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

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

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

Проверьте ответ Джо для решения Reflection.

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

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

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

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

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

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

class MyClass : ICloneable
{
public MyClass()
{

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

return mc;
}

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

   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);
   }

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

Программа класса
    {
        static void Main(строка [] args)
        {
            Console.WriteLine("Привет");
            ExecutionReport er = новый ExecutionReport("ORDID1234",3000.43,DateTime.UtcNow);
            Order ord = новый Order();
            ord = er;
            Console.WriteLine("Передаваемые значения: " + er.OrderId + "\t" + ord.Amount.ToString() + "\t" + ord.TimeStamp.ToString() + "\t");
            Console.ReadLine();
        }
    }


    общественный порядок
    {
        публичная строка OrderId {get; задавать; }
        public double Amount { get; задавать; }
        public DateTime TimeStamp { get; задавать; }
        открытый статический неявный оператор ExecutionReport(Order ord)
        {
            вернуть новый ExecutionReport()
            {
                OrderId = ord.OrderId,
                Сумма = ord.Amount,
                TimeStamp = ord.TimeStamp
            };
        }
        публичный статический неявный оператор Order(ExecutionReport er)
        {
            вернуть новый заказ ()
            {
                OrderId = er.OrderId,
                Сумма = er.Amount,
                TimeStamp = er.TimeStamp
            };
        }

        общественный порядок()
        {}
    }

    открытый класс ExecutionReport
    {
        публичная строка OrderId {get; задавать; }
        public double Amount { get; задавать; }
        public DateTime TimeStamp { get; задавать; }
        public ExecutionReport() { }
        public ExecutionReport(строка orderId, двойная сумма, DateTime ts)
        {
            OrderId = orderId; Сумма = сумма; TimeStamp = ts;
        }
    }

user12950 можно расширить с помощью статического универсального метода, определенного в интерфейсе:

      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));
        }
    }
}

Классы, реализующиеIPerson:

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

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

Использование:

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

IPerson.CopyProperties(worker, manager);

В качестве альтернативы вы можете использоватьnewограничение для создания экземпляра желаемого типа, учитывая, что он реализует интерфейс:

       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;
     }
 }
 

Использование:

      var worker = new Worker { Name = "John" };
var manager = IPerson.ChangeType<Manager, Worker>(worker);
Другие вопросы по тегам