Каков наилучший способ объединения двух объектов во время выполнения с использованием C #? - PullRequest
6 голосов
/ 31 августа 2009

У меня есть два объекта, и я хочу объединить их:

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 #, поэтому я не совсем уверен во всех доступных мне опциях.

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

EDIT


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

Я предложил два варианта:

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

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

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

EDIT


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

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

Ответы [ 7 ]

8 голосов
/ 31 августа 2009

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

public class FooEx<T>
{
    public Foo Foo { get; set; }
    public T Ex { get; set; }
}
3 голосов
/ 31 августа 2009

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

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));
2 голосов
/ 31 августа 2009

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

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

1 голос
/ 31 августа 2009

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

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

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

Поскольку все, что вас интересует, это свойства, то довольно просто использовать эту статью в качестве примера. Оставьте il для создания методов, и все готово.

0 голосов
/ 01 сентября 2009

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

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

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 для нескольких подклассов.

Десериализация должна заключаться в проверке элементов XmlElements, а затем в поиске подходящего класса или, возможно, в использовании пространства имен для поиска класса.

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

0 голосов
/ 01 сентября 2009

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

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 голосов
/ 31 августа 2009

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

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

...