2009-08-31 2 views
6

У меня есть два объекта, и я хочу, чтобы объединить их:Каков наилучший способ объединить два объекта во время выполнения с помощью C#?

public class Foo 
{ 
    public string Name { get; set; } 
} 

public class Bar 
{ 
    public Guid Id { get; set; } 
    public string Property1 { get; set; } 
    public string Property2 { get; set; } 
    public string Property3 { get; set; } 
    public string Property4 { get; set; } 
} 

Для создания:

public class FooBar 
{ 
    public string Name { get; set; } 
    public Guid Id { get; set; } 
    public string Property1 { get; set; } 
    public string Property2 { get; set; } 
    public string Property3 { get; set; } 
    public string Property4 { get; set; } 
} 

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

Что было бы лучшим способом сделать это? Можно ли это сделать с помощью LINQ Expressions или мне нужно генерировать его динамически или есть другой способ? Я все еще изучаю новое пространство имен LINQ в C# 3.0, поэтому извините невежество, если это невозможно сделать с помощью LINQ Expressions. Это также первый случай, когда мне когда-либо приходилось делать что-то такое с C#, поэтому я не совсем уверен в всех доступных мне вариантах.

Спасибо за любые предоставленные варианты.

EDIT


Это строго для добавления мета-информации типа вверенному мне для сериализации. Этот сценарий не позволяет объектам пользователя не знать метаинформацию, которая должна быть добавлена ​​до ее сериализации. Я задал два варианта, прежде чем задавать этот вопрос, и я просто хотел узнать, есть ли еще, прежде чем принимать решение о том, какой из них использовать.

Два варианта я придумываю является:

Манипулирования сериализованной строки типа вверенному мне после сериализации, путем добавления меты-информации.

Обертывание имеющегося у меня типа, похожего на то, что упомянуто @Zxpro, но мое немного отличалось, что хорошо. Это будет просто сделать пользователь моего API должен следовать соглашению, что не плохо, поскольку все о конвенции по конфигурации:

public class Foo<T> 
{ 
    public string Name { get; set; } 
    public T Content { get; set; } 
} 

EDIT


Спасибо всем за их ответы. Я решил обернуть объект, как указано выше, и я дал ответ на @Zxpro, так как большинству понравился этот подход.

Если кто-либо другой сталкивается с этим вопросом, не стесняйтесь публиковать сообщения, если вы считаете, что может быть лучший способ.

ответ

8

Если вы не возражаете, чтобы они были сгруппированы, а не слиты:

public class FooEx<T> 
{ 
    public Foo Foo { get; set; } 
    public T Ex { get; set; } 
} 
+1

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

+0

@ Randalpho: Может быть, может и нет. Это не может быть известно * в этом контексте *, но вполне возможно, в зависимости от архитектуры, использовать общий. –

+0

Я думал об этом, и это мой план возврата, если это невозможно сделать легко и результативно. Однако шахта несколько отличалась. В основном создание public Foo {public string Имя {get; задавать; } public T Content {get; задавать; }} –

2

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

Когда .NET 4 выходит, есть новая библиотека динамического времени выполнения, которая может вам помочь.

+0

Я обязательно проверю новый динамический материал на C# 4, когда у меня появится шанс. –

1

Помимо вопроса «Почему», единственный способ, которым я могу думать, чтобы взять два объекта, один известный и один неизвестный , и объединить их в новый тип будет использовать Reflection.Emit для генерации нового типа во время выполнения.

В MSDN есть примеры. Вам нужно будет определить погоду, которую вы хотели бы объединить поля с тем же именем или иметь известный тип, заменяющий неизвестный тип.

Насколько я могу судить, в LINQ нет способа сделать это.

Поскольку все, что вас интересует, это свойство, оно должно быть довольно простым в использовании в качестве примера this article. Оставьте ИЛ для создания методов, и вам хорошо идти.

+0

«Почему», предназначен для сериализации в один тип для постоянного хранения. Это помешало бы мне не вставлять объекты, как в мой комментарий к Zxpro. –

+0

Почему бы просто не сериализовать первый, а другой - в тот же поток или что-то еще? –

+0

@ Антон Тихый - Это один из вариантов, который я использовал. Я просто ищу что-нибудь еще, что может быть лучше, вместо того, чтобы манипулировать строками, в моем случае. –

0

Как указывали другие, нет возможности «слить» их (если вы думаете о select * с несколькими таблицами в SQL, например). Ваш ближайший аналог будет брать маршрут, который Zxpro предоставил, и «сгруппировать» их в родовом классе.

Что именно вы хотели бы выполнить с «слиянием» их? Объявление свойств явно будет иметь наибольший эффект удобства при написании кода и безопасности во время компиляции, но если вы не можете указать тип, то для этого нет никакой возможности. Если вы просто ищете общий контейнер с «мешком свойств», тогда существующая структура данных, такая как Dictionary<T,T> или Hashtable, должна иметь возможность справиться с этим.

3

UNTESTED, но с использованием Reflection.Emit API, что-то, как это должно работать:

public Type MergeTypes(params Type[] types) 
{ 
    AppDomain domain = AppDomain.CurrentDomain; 
    AssemblyBuilder builder = 
     domain.DefineDynamicAssembly(new AssemblyName("CombinedAssembly"), 
     AssemblyBuilderAccess.RunAndSave); 
    ModuleBuilder moduleBuilder = builder.DefineDynamicModule("DynamicModule"); 
    TypeBuilder typeBuilder = moduleBuilder.DefineType("CombinedType"); 
    foreach (var type in types) 
    { 
     var props = GetProperties(type); 
     foreach (var prop in props) 
     { 
      typeBuilder.DefineField(prop.Key, prop.Value, FieldAttributes.Public); 
     } 
    } 

    return typeBuilder.CreateType(); 


} 

private Dictionary<string, Type> GetProperties(Type type) 
{ 
    return type.GetProperties().ToDictionary(p => p.Name, p => p.PropertyType); 
} 

ПРИМЕНЕНИЕ:

Type combinedType = MergeTypes(typeof(Foo), typeof(Bar)); 
+0

Спасибо за код, я ценю ваш вклад. Я всегда знал Reflection.Emit было жизнеспособным решением, но я надеялся, что могут быть другие способы. Я обязательно буду помнить об этом, если обертывание типа не получится. –

0

Если вы можете добавить метод в класс метаданных вы могли бы сделать что-то например,

public class Foo 
{ 
    public string Name { get; set; } 
    public void SerializeWithMetadata(Bar bar) 
    { 
     var obj = new { 
         Name = this.Name, 
         Guid = bar.Guid, 
         Property1 = Bar.Property1 
         } 
     //Serialization code goes here 
    } 
} 

public class Bar 
{ 
    public Guid Id { get; set; } 
    public string Property1 { get; set; } 
    public string Property2 { get; set; } 
    public string Property3 { get; set; } 
    public string Property4 { get; set; } 
} 

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

+0

Проблема в том, что ни Foo, ни Bar не известны до запуска, и ваш код зависит от знания этой информации. –

+0

Я не вижу, как тип объектов может быть неизвестен во время выполнения (это будет означать, что они не создаются), если вы считаете, что не все комбинации foo и bar известны в _compile time_ (или число слишком велико), тогда результат тот же –

+0

«ни Foo, ни Bar не известны до времени исполнения» ... что я имел в виду, так это то, что вы предположили, что в баре есть Guid и Property1 во время компиляции, и плакат задал конкретно, как объединить типы которые неизвестны во время компиляции. –

0

Другой вариант:

Изменить первый класс, как так

public class Foo 
{ 
    public string Name { get; set; } 

    [System.Xml.Serialization.XmlAnyElementAttribute()] 
    public XmlElement Any {get;set;} 
} 

Возьмите второй класс и сериализовать его в XmlElement как так:

XmlElement SerializeToElement(Type t, object obj) 
{ 
    XmlSerializer ser = new XmlSerializer(t); 
    StringWriter sw = new StringWriter(); 
    using (XmlWriter writer = XmlWriter.Create(sw, settings)) 
     ser.Serialize(writer, obj); 

    string val = sw.ToString(); 

    XmlDocument doc = new XmlDocument(); 
    doc.LoadXml(xmlString); 

    return (XmlElement)doc.DocumentElement; 

} 

Установить свойство Любой в XmlElement и сериализовать Вы увидите XML для другого класса наб edded в документе, в комплекте со всеми пространствами имен

Вы даже можете уйти с этим, если класс был создан с помощью Xsd.exe, если вы используете следующее в качестве элемента:

<xs:any namespace="##any" processContents="lax" /> 

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

Дезертификация должна быть проверкой XmlElements, а затем поиск подходящего класса или, возможно, использование пространства имен для поиска класса.

Это намного более аккуратный, чем возиться со строковыми манипуляциями.

 Смежные вопросы

  • Нет связанных вопросов^_^