Каков наилучший способ объединить два объекта во время выполнения с помощью 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 или мне нужно генерировать его динамически или есть другой способ? Я все еще изучаю новое пространство имен LINQ в C# 3.0, поэтому извините невежество, если это не может быть сделано с помощью выражений LINQ. Это также первый раз, когда мне приходилось делать что-то динамичное, как это с C#, поэтому я не совсем уверен во всех вариантах, которые у меня есть.

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

редактировать


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

два варианта, которые я придумал:

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

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

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

редактировать


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

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

7 ответов


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

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

непроверенный, но с использованием отражения.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));

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

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


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

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

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

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


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

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


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

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

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


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

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

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;

}

установите свойство Any в XmlElement и сериализуйте его Вы увидите XML для другого класса, встроенного в документ, в комплекте со всеми пространствами имен

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

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

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

Deserializaing должен быть вопросом проверки XmlElements и затем поиска соответствующего класса или, возможно, использования пространства имен для поиска класса.

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