2009-08-27 30 views
12

Tôi ghét có một loạt các phương pháp "trái/phải". Mỗi khi một thuộc tính được thêm vào hoặc bị xóa, tôi phải sửa từng phương thức. Và bản thân mã chỉ trông ... sai.Làm thế nào để sử dụng sự phản chiếu để đơn giản hóa các nhà thầu và so sánh?

public Foo(Foo other) 
{ 
    this.Bar = other.Bar; 
    this.Baz = other.Baz; 
    this.Lur = other.Lur; 
    this.Qux = other.Qux; 
    this.Xyzzy= other.Xyzzy; 
} 

Thực sự đây chỉ là một vòng lặp chưa được kiểm soát lặp qua các thuộc tính, sao chép chúng giữa các đối tượng. Vậy tại sao không thành thật về thực tế đó? Phản ánh để giải cứu!

public Foo(IFoo other) 
{ 
    foreach (var property in typeof(IFoo).GetProperties()) 
    { 
     property.SetValue(this, property.GetValue(other, null), null); 
    } 
} 

Tôi có thể đang cố ép mô hình tôi học được từ Lua lên C#, nhưng ví dụ cụ thể này dường như không quá có mùi với tôi. Từ đây, tôi bắt đầu làm một số thứ phức tạp hơn, nhạy cảm với thứ tự của các trường. Ví dụ, thay vì phải một chồng hầu như giống hệt if báo cáo để soạn một chuỗi từ các lĩnh vực, tôi chỉ lặp qua chúng theo thứ tự mong muốn:

public override string ToString() 
{ 
    var toJoin = new List<string>(); 
    foreach (var property in tostringFields) 
    { 
     object value = property.GetValue(this, null); 
     if (value != null) 
      toJoin.Add(value.ToString()); 
    } 
    return string.Join(" ", toJoin.ToArray()); 
} 
private static readonly PropertyInfo[] tostringFields = 
{ 
    typeof(IFoo).GetProperty("Bar"), 
    typeof(IFoo).GetProperty("Baz"), 
    typeof(IFoo).GetProperty("Lur"), 
    typeof(IFoo).GetProperty("Qux"), 
    typeof(IFoo).GetProperty("Xyzzy"), 
}; 

Vì vậy, bây giờ tôi có iterability tôi muốn, nhưng tôi vẫn có ngăn xếp mã phản chiếu từng thuộc tính mà tôi quan tâm (Tôi cũng làm việc này cho CompareTo, sử dụng một tập hợp thuộc tính khác theo thứ tự khác). Tồi tệ hơn đó là việc đánh máy mạnh. Điều này thực sự bắt đầu có mùi.

Vậy còn việc sử dụng các thuộc tính trên mỗi thuộc tính để xác định thứ tự thì sao? Tôi bắt đầu xuống con đường này và thực sự nó hoạt động tốt, nhưng nó chỉ làm cho toàn bộ điều trông cồng kềnh. Nó hoạt động rất tốt về mặt ngữ nghĩa, nhưng tôi luôn cảnh giác với việc sử dụng các tính năng nâng cao chỉ vì chúng "gọn gàng". Sử dụng sự phản chiếu theo cách này có quá mức cần thiết không? Có một số giải pháp khác cho vấn đề mã trái/phải tôi đang thiếu?

+0

i'dd thêm một thẻ ngôn ngữ để có được nhiều người xem – Toad

+0

Hãy ghi nhớ, phản xạ luôn luôn đi kèm với hiệu suất một hit trong C#. Bạn sẽ thao tác Foos thường xuyên như thế nào? Một giải pháp lai có thể là sử dụng Reflection.Emit, và cache mã kết quả - khởi động chậm hơn, thực hiện nhanh. – zildjohn01

+1

C# 4.0 đang được xây dựng trong trình biên dịch, có thể hữu ích trong việc tạo mã và sử dụng nó, bây giờ chúng tôi cũng có vấn đề tương tự nhưng chúng tôi đã thiết kế giải pháp bằng cách sử dụng cấu trúc lớp XML và chúng tôi sử dụng phương pháp tạo mã của riêng mình tạo ra rất nhiều phương thức khác phụ thuộc vào tên của memebers, về cơ bản nó là một ORML nhưng làm mọi thứ tự động. –

Trả lời

5

Sử dụng sự phản chiếu trong chính nó không phải là xấu, nhưng bạn sẽ có một hiệu suất hit đặc biệt là nếu bạn làm điều đó đệ quy.

Tôi không phải là người hâm mộ của các nhà xây dựng bản sao được mã hóa cứng vì các nhà phát triển quên cập nhật chúng khi họ thêm các thuộc tính mới vào một lớp.

Có nhiều cách khác để hoàn thành những gì bạn muốn, bao gồm Marc Gravells Hyper Property Descriptor hoặc nếu bạn muốn tìm hiểu một số IL và OPCodes, bạn có thể sử dụng System.Reflection.Emit hoặc thậm chí Cecil from Mono.

Dưới đây là một ví dụ của việc sử dụng Hyper tài sản Descriptor mà bạn có thể có thể cho phù hợp với nhu cầu của bạn:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using Hyper.ComponentModel; 
namespace Test { 
    class Person { 
     public int Id { get; set; } 
     public string Name { get; set; } 
    } 
    class Program { 
     static void Main() { 
      HyperTypeDescriptionProvider.Add(typeof(Person)); 
      var properties = new Dictionary<string, object> { { "Id", 10 }, { "Name", "Fred Flintstone" } }; 
      Person person = new Person(); 
      DynamicUpdate(person, properties); 
      Console.WriteLine("Id: {0}; Name: {1}", person.Id, person.Name); 
      Console.ReadKey(); 
     } 
     public static void DynamicUpdate<T>(T entity, Dictionary<string, object> { 
      foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(typeof(T))) 
       if (properties.ContainsKey(propertyDescriptor.Name)) 
        propertyDescriptor.SetValue(entity, properties[propertyDescriptor.Name]); 
     } 
    } 
} 

Nếu bạn quyết định tiếp tục sử dụng phản chiếu, bạn có thể làm giảm hiệu suất trúng bộ nhớ đệm các cuộc gọi của bạn để GetProperties () như sau:

public Foo(IFoo other) { 
    foreach (var property in MyCacheProvider.GetProperties<IFoo>()) 
     property.SetValue(this, property.GetValue(other, null), null); 
} 
+3

Type.GetProperties đã có bộ nhớ đệm của riêng nó (thời gian cuộc gọi đầu tiên và các cuộc gọi tiếp theo). Các hit hiệu suất thực sự của sự phản ánh là các cuộc gọi property.SetValue, mà bạn có thể giảm thiểu với MSIL thế hệ vv –

+0

@Rob Fonseca-Ensor Cảm ơn, tôi không biết điều đó. – grenade

+0

Hmm ... nếu hiệu suất đạt được trong việc thiết lập các giá trị thì điều này có lẽ không phải là cách để đi. Cố gắng giải quyết tất cả những thứ cấp thấp đó chắc chắn sẽ quá mức cần thiết. Cảm ơn các đầu vào. – Cogwheel

2

IMHO, phản ánh là một tính năng rất mạnh mẽ của C#, nhưng đó là rất có khả năng dẫn đến mã cồng kềnh, và có thêm nhiều điều để các đường cong học tập của mã và làm giảm khả năng bảo trì. Bạn sẽ có nhiều khả năng phạm sai lầm (một khi việc tái cấu trúc cơ bản có thể dẫn đến lỗi), và sợ thay đổi tên của bất kỳ tài sản nào (nếu bạn tình cờ tìm được tên tốt hơn) hoặc những thứ tương tự.

Cá nhân tôi có mã có vấn đề tương tự và tôi có cùng ý tưởng thêm thuộc tính để duy trì trật tự và v.v. Nhưng nhóm của tôi (kể cả tôi) nghĩ tốt hơn là mất thời gian thay đổi thiết kế không cần điều này. Có lẽ vấn đề này là do thiết kế xấu (tốt, nó đã được trong trường hợp của tôi, nhưng tôi không thể nói giống nhau về bạn).

+0

Tôi không chắc làm thế nào nó sẽ dễ bị lỗi hơn khi thay đổi các thuộc tính kể từ khi toàn bộ điểm là loại bỏ sự cần thiết phải tham chiếu tên thuộc tính trực tiếp trong mã. Ngay bây giờ tôi chỉ xây dựng một mô hình miền sẽ được ánh xạ tới cơ sở dữ liệu, vì vậy việc thêm/xóa/đổi tên thuộc tính sẽ hầu như không ngừng trong quá trình phát triển (đặc biệt là vì tôi đang thử TDD lần đầu tiên) – Cogwheel

+0

Bạn có "typeof (IFoo) .GetProperty (" Bar ")" trong mã của bạn, tham chiếu tên thuộc tính trực tiếp –

+0

Do đó hai đoạn cuối cùng trong câu hỏi của tôi;) – Cogwheel

3

Tôi biết đã có câu trả lời cho điều này, nhưng tôi muốn chỉ ra rằng có một thư viện kết hợp một vài chiến lược giảm thiểu tác động đến hiệu suất mà một vài người đã thảo luận.

Thư viện được gọi là AutoMapper và nó ánh xạ từ một đối tượng này sang đối tượng khác và làm như vậy bằng cách tạo động một assembly IL khi đang bay. Điều này đảm bảo rằng không phải là một thời gian hit đầu tiên, bạn sẽ có được hiệu suất cao và mã của bạn sẽ đơn giản hơn nhiều:

public Foo(Foo other) 
{ 
    Mapper.Map(other, this); 
} 

này có xu hướng làm việc tuyệt vời và có thêm tiền thưởng không được phát minh ở đây, mà tôi là một fan hâm mộ của.

Tôi đã thực hiện một số thử nghiệm hiệu suất và sau lần truy cập đầu tiên 20 ms (vẫn khá nhanh), nó gần bằng 0 khi bạn có thể nhận được. Khá ấn tượng.

Hy vọng điều này sẽ giúp ai đó.

+0

Hoặc bạn có thể sử dụng emitmapper thậm chí còn nhanh hơn. –

+0

Tôi không nghĩ rằng emitmapper đã có sẵn trong '09 khi tôi gửi này :) –

+0

Vâng ... nó không bao giờ muộn để cải thiện câu trả lời của bạn :) –

2

Vấn đề cơ bản là bạn đang cố sử dụng ngôn ngữ được nhập tĩnh như kiểu được nhập động.

Không thực sự cần bất kỳ thứ gì lạ mắt. Nếu bạn muốn có thể lặp lại các thuộc tính, bạn có thể sử dụng Bản đồ <> làm cửa hàng sao lưu cho tất cả các thuộc tính trong lớp của bạn.

Thật trùng hợp đây là cách trình thủ thuật dự án VS áp dụng các cài đặt ứng dụng cho bạn. (Xem System.Configuration.ApplicationSettingsBase) của nó cũng rất 'lua-like'

public bool ConfirmSync { 
     get { 
      return ((bool)(this["ConfirmSync"])); 
     } 
     set { 
      this["ConfirmSync"] = value; 
     } 
    } 
+0

Nice! Chiến lược rất thú vị. Điều này sẽ cho phép bạn lặp qua cửa hàng sao lưu và sao chép tất cả mọi thứ trong một vòng lặp rất đơn giản. Rất sáng tạo. –

+0

Cảm ơn lời khuyên. Tôi có lẽ sẽ kết thúc bằng cách sử dụng này tại một số điểm, nhưng nó không hoàn toàn phù hợp với nhiệm vụ hiện tại của tôi. – Cogwheel

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