Использование интерфейса для преобразования объекта из одного типа в другой?
Предположим, у меня есть два класса с одинаковым интерфейсом:
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);