2009-08-31 30 views
6

Tôi có hai đối tượng và tôi muốn kết hợp chúng:Cách tốt nhất để hợp nhất hai đối tượng trong thời gian chạy bằng C# là gì?

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

Để tạo:

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

tôi sẽ chỉ biết cấu trúc của Foo khi chạy. Bar có thể là bất kỳ kiểu nào khi chạy. Tôi muốn có một phương pháp sẽ được đưa ra một loại và nó kết hợp kiểu đó với Foo. Ví dụ, kịch bản trên, phương thức này được đưa ra một kiểu Bar khi chạy và tôi kết hợp nó với Foo.

Cách tốt nhất để làm điều này là gì? Nó có thể được thực hiện bằng cách sử dụng LINQ Expressions hoặc tôi có để tạo ra nó tự động hoặc là có một cách khác? Tôi vẫn đang học không gian tên LINQ mới trong C# 3.0, vì vậy xin lỗi vì sự thiếu hiểu biết nếu nó không thể được thực hiện bằng cách sử dụng LINQ Expressions. Đây cũng là lần đầu tiên tôi phải làm một cái gì đó năng động như thế này với C#, vì vậy tôi không hoàn toàn chắc chắn về tất cả các tùy chọn tôi có sẵn cho tôi.

Cảm ơn bạn đã chọn bất kỳ tùy chọn nào.

EDIT


Đây là nghiêm chỉnh để thêm thông tin meta vào loại nhất định với tôi cho serialization. Kịch bản này giữ cho các đối tượng của người dùng không biết về thông tin meta cần được thêm vào, trước khi nó được tuần tự hóa. Tôi đã đưa ra hai lựa chọn trước khi đặt câu hỏi này và tôi chỉ muốn xem liệu có còn nữa không, trước khi quyết định sử dụng cái nào.

Hai tùy chọn Tôi đã đưa ra là:

Thao tác chuỗi tuần tự của các loại trao cho tôi sau khi serializing nó, bằng cách thêm các thông tin meta.

Bao gói loại cho tôi, giống như những gì @Zxpro đề cập, nhưng tôi hơi khác một chút, điều đó là tốt. Nó sẽ chỉ làm cho người dùng API của tôi phải làm theo quy ước, mà không phải là một điều xấu, bởi vì tất cả mọi người là về ước về cấu hình:

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

EDIT


Cảm ơn tất cả mọi người cho câu trả lời của họ. Tôi quyết định đóng gói các đối tượng như trên và tôi đã đưa ra câu trả lời cho @Zxpro, vì đa số đều thích cách tiếp cận đó.

Nếu có ai khác gặp phải câu hỏi này, vui lòng đăng bài, nếu bạn nghĩ rằng có thể có cách tốt hơn.

Trả lời

8

Nếu bạn không nhớ chúng được nhóm lại chứ không phải là sáp nhập:

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

Đây sẽ là một giải pháp lý tưởng, nhưng nó sẽ đòi hỏi kiến ​​thức biên dịch về Bar, mà người hỏi ban đầu đã tuyên bố chỉ có sẵn khi chạy. – Randolpho

+0

@Randalpho: Có thể, có thể không. Nó có thể không được biết * trong bối cảnh đó *, nhưng nó cũng có thể có thể, tùy thuộc vào kiến ​​trúc, để sử dụng chung chung. –

+0

Tôi đã nghĩ về điều đó và đó là kế hoạch rơi trở lại của tôi, nếu điều này không thể được thực hiện dễ dàng và performant. Mỏ của tôi hơi khác một chút. Về cơ bản, hãy công khai Foo {public string Name {get; bộ; } nội dung công khai T {get; bộ; }} –

2

Thật không may, đây không phải là điều bạn có thể làm dễ dàng. Điều tốt nhất bạn có thể làm là tạo một loại ẩn danh như một phần của truy vấn LINQ, nhưng chỉ có phạm vi cục bộ và do đó sẽ chỉ phù hợp với bạn trong phương pháp mà bạn thực hiện.

Khi .NET 4 xuất hiện, có Thư viện thời gian động mới có thể giúp bạn.

+0

Tôi chắc chắn sẽ xem nội dung động mới trong C# 4 khi tôi có cơ hội. –

1

Bên cạnh những câu hỏi "Tại sao", cách duy nhất tôi có thể nghĩ đến mất hai đối tượng, một nổi tiếng và là một trong vô danh và kết hợp chúng thành một kiểu mới sẽ là sử dụng Reflection.Emit để tạo ra một kiểu mới khi chạy.

Có các ví dụ về MSDN. Bạn sẽ phải xác định thời tiết bạn muốn hợp nhất các trường có cùng tên hoặc có loại đã biết thay thế loại không xác định.

Theo như tôi có thể biết, không có cách nào để thực hiện việc này trong LINQ.

Vì tất cả những gì bạn quan tâm là Thuộc tính, nên dễ sử dụng this article làm ví dụ. Để lại il cho việc tạo ra các phương pháp và bạn tốt để đi.

+0

"Tại sao", là để tuần tự hóa thành một loại để lưu trữ liên tục. Nó sẽ giữ cho tôi khỏi phải làm tổ các đối tượng, như trong bình luận của tôi cho Zxpro. –

+0

Tại sao không chỉ serialize đầu tiên, sau đó khác vào cùng một dòng/bất cứ điều gì? –

+0

@Anton Tykhyy - Đó là một trong các tùy chọn tôi đã sử dụng. Tôi chỉ đang tìm kiếm bất kỳ thứ gì khác có thể tốt hơn, thay vì thao túng dây, trong trường hợp của tôi. –

0

Như những người khác đã chỉ ra, không có cách nào để "hợp nhất" chúng (nếu bạn đang nghĩ đến một số select * với nhiều bảng trong SQL chẳng hạn). Tương tự gần nhất của bạn sẽ được lấy tuyến đường mà Zxpro đã cung cấp và "nhóm" chúng trong một lớp chung chung.

Chính xác, bạn muốn thực hiện điều gì với việc "hợp nhất" chúng? Khai báo các thuộc tính một cách rõ ràng sẽ có hiệu ứng thuận tiện lớn nhất về viết mã và an toàn biên dịch-thời gian, nhưng nếu bạn không thể chỉ định một kiểu thì không có cơ hội cho điều đó. Nếu bạn chỉ đang tìm một hộp chứa "túi thuộc tính" chung, thì cấu trúc dữ liệu hiện có, chẳng hạn như Dictionary<T,T> hoặc Hashtable sẽ có thể xử lý được.

3

chưa được kiểm tra, nhưng sử dụng API Reflection.Emit, một cái gì đó như thế này nên làm việc:

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

SỬ DỤNG:

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

Cảm ơn bạn đã viết mã, tôi đánh giá cao ý kiến ​​của bạn. Tôi luôn luôn biết Reflection.Emit là một giải pháp khả thi, nhưng tôi đã hy vọng có thể có những cách khác. Tôi chắc chắn sẽ giữ điều này trong tâm trí, nếu gói các loại không làm việc ra ngoài. –

0

Nếu bạn có thể thêm một phương pháp để lớp siêu dữ liệu bạn có thể làm điều gì đó như sau

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

Tôi không chắc chắn tôi muốn giới thiệu phương pháp tiếp cận chính xác này tôi chủ yếu còn lại nó ở đây để hiển thị các kiểu nặc danh như là một lựa chọn có thể là có thể là giá trị khai thác

+0

Vấn đề là cả Foo lẫn Bar đều không được biết trước khi chạy, và mã của bạn phụ thuộc vào việc biết thông tin đó. –

+0

Tôi không biết cách loại đối tượng có thể không biết trong thời gian chạy (có nghĩa là chúng không được khởi tạo) bao giờ nếu bạn cho rằng không phải tất cả các kết hợp của foo và bar đều được biết tại _compile time_ (hoặc số quá lớn) kết quả là cùng một –

+0

"không phải Foo và Bar được biết trước khi chạy" ... ý tôi là bạn đã giả định rằng Bar có một Guid và Property1, tại thời gian biên dịch và áp phích được hỏi cụ thể về cách hợp nhất các loại không biết lúc biên dịch. –

0

Một Lựa Chọn:

Sửa đổi các lớp đầu tiên như vậy

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

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

Lấy lớp thứ hai và serialize nó thành một XmlElement như như vậy:

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; 

} 

Đặt thuộc tính Any to the XmlElement và serialize nó Bạn sẽ thấy XML cho lớp khác emb edded trong tài liệu, hoàn chỉnh với tất cả các không gian tên

Bạn thậm chí có thể nhận được ngay với điều này nếu các lớp được tạo ra sử dụng xsd.exe nếu bạn sử dụng sau đây như một yếu tố:

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

Tôi tin rằng bạn có thể cũng lấy đi một mảng XmlElements cho nhiều hơn một lớp con.

Deserializaing phải là vấn đề kiểm tra XmlElements và sau đó tìm kiếm một lớp phù hợp hoặc có thể sử dụng không gian tên để tìm lớp.

Điều này dễ hiểu hơn nhiều so với thao tác chuỗi.

Các vấn đề liên quan