2010-07-29 31 views
6

Tôi muốn xây dựng một phương thức chấp nhận một tham số chuỗi, và một đối tượng mà tôi muốn trả về một thành viên cụ thể dựa trên param. Vì vậy, phương pháp đơn giản nhất là để xây dựng một câu lệnh switch:tuyên bố chuyển đổi được lập chỉ mục hoặc tương đương? .net, C#

public GetMemberByName(MyObject myobj, string name) 
{ 
    switch(name){ 
    case "PropOne": return myobj.prop1; 
    case "PropTwo": return myobj.prop2; 
    } 
} 

này hoạt động tốt, nhưng tôi có thể gió lên với một danh sách khá lớn ... Vì vậy, tôi đã tò mò nếu có một cách, mà không cần viết một loạt các lồng nhau các cấu trúc if-else, để thực hiện điều này theo cách được lập chỉ mục, sao cho trường phù hợp được tìm thấy theo chỉ mục thay vì rơi qua một chuyển đổi cho đến khi tìm thấy kết quả phù hợp.

Tôi xem xét sử dụng Dictionary<string, something> để truy cập nhanh vào các chuỗi phù hợp (là thành viên chủ chốt) nhưng vì tôi muốn truy cập thành viên của đối tượng được truyền vào, tôi không chắc chắn cách thực hiện điều này .

  • Tôi đang cố gắng tránh phản ánh vv để có triển khai nhanh. Tôi có thể sẽ sử dụng tạo mã, do đó giải pháp không cần phải nhỏ/chặt chẽ, v.v.

  • Tôi đã xây dựng từ điển nhưng mỗi đối tượng đã khởi tạo nó. Vì vậy, tôi bắt đầu di chuyển điều này đến một phương pháp duy nhất có thể tìm kiếm các giá trị dựa trên các phím-một tuyên bố chuyển đổi. Nhưng kể từ khi tôi không còn lập chỉ mục, tôi sợ các tra cứu liên tục gọi phương pháp này sẽ chậm.

  • SO: Tôi đang tìm cách kết hợp hiệu suất của tra cứu được lập chỉ mục/băm (như sử dụng từ điển) với các thuộc tính trả về cụ thể của đối tượng được truyền vào. Tôi có thể sẽ đặt điều này trong một phương pháp tĩnh trong mỗi lớp nó được sử dụng cho.

+0

Vì myobj có vẻ rất cụ thể (trái ngược với một 'đối tượng'), không phải là myobj.Property đủ thay vì GetMemberByName (myobj," Property ")? – Humberto

+0

Chỉ là một ý nghĩ, tôi hoàn toàn hiểu lầm ý định của bạn dựa trên tiêu đề. Ví dụ đã xóa nó cho tôi. Có vẻ như câu hỏi là giống như "Làm thế nào tôi có thể truy cập một tài sản của một đối tượng theo tên", phải không? –

+0

Tại sao 'name' là một chuỗi? Đây có phải là thứ được người dùng nhập vào không? Là nó từ một tập tin? Có lý do nào đó không phải là 'enum'? –

Trả lời

7

Dưới đây là một cách dễ dàng, bạn có thể sử dụng một từ điển:

Dictionary<string, Func<MyObject, object>> propertyNameAssociations; 

    private void BuildPropertyNameAssociations() 
    { 
     propertyNameAssociations = new Dictionary<string, Func<MyObject, object>>(); 
     propertyNameAssociations.Add("PropOne", x => x.prop1); 
     propertyNameAssociations.Add("PropTwo", x => x.prop2); 
    } 

    public object GetMemberByName(MyObject myobj, string name) 
    { 
     if (propertyNameAssociations.Contains(name)) 
      return propertyNameAssociations[name](myobj); 
     else 
      return null; 
    } 
+0

Không tệ, bạn thậm chí có thể sử dụng sự phản chiếu để tạo mã. Các chỉ báo trước là thiếu sử dụng chung chung. – ChaosPandion

+0

@ChaosPandion - Vâng, tôi hy vọng rằng kiểu trả về sẽ là một cái gì đó cụ thể, nhưng điều đó thật đáng buồn được bỏ qua trong câu hỏi. Có lẽ OP có thể thay thế "đối tượng" với một cái gì đó tốt hơn! –

+0

Jeffrey, chỉ là những gì tôi đang tìm kiếm, cảm ơn ngài. –

2

Bạn có thể sử dụng reflection để nhận thuộc tính động trong thời gian chạy. Đây là một đoạn trích từ một tiện ích relection nhỏ tôi đã viết. Này được viết như một phương pháp mở rộng đó sẽ dễ dàng cho phép bạn để có được một tài sản từ dụ lớp học của bạn

myInstance.GetProperty<string>("Title"); // Replace 'string' with the proper return value. 

Mã:

public static class ReflectionExtensions 
{ 
    private const BindingFlags DefaultFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; 

    public static T GetProperty<T>(this object instance, string propertyName) 
    { 
     PropertyInfo property = GetPropertyInfo(instance, propertyName); 
     if (property == null) 
     { 
      var message = string.Format("The Type, '{0}' does not implement a '{1}' property", instance.GetType().AssemblyQualifiedName, propertyName); 
      throw new NotImplementedException(message); 
     } 

     return (T)property.GetValue(instance, null); 
    } 

    private static PropertyInfo GetPropertyInfo(object instance, string propertyName) 
    { 
     Type type = instance.GetType(); 
     return type.GetProperty(propertyName, DefaultFlags); 
    } 
} 
+0

+1, gợi ý tuyệt vời để sử dụng phương pháp mở rộng chung. –

+0

Một nhược điểm, mặc dù (xem bình luận của tôi trên bài viết của Justin Niessner) là nếu kiểu trả về là chung, bạn phải xác định nó trong lời gọi phương thức, vì trình biên dịch không thể suy ra kiểu T bằng cách sử dụng giá trị trả về một mình. Vì vậy, cuộc gọi phương thức của bạn sẽ thực sự là MyInstance.GetProperty ("Tiêu đề") hoặc bất kỳ loại thuộc tính nào. –

+0

@ John M Gant, bạn đã đúng, tôi đã cập nhật mẫu của mình. –

0

Vâng, giả định rằng tên phù hợp với tên thực của bất động sản (không giống như ví dụ của bạn), điều này có lẽ sẽ được xử lý tốt nhất thông qua sự phản chiếu.

0

Bạn không thể làm điều đó với chỉ mục, nhưng bạn có thể sử dụng sự phản chiếu.

8

Dưới đây là một mockup nhanh chóng của một cái gì đó mà có thể làm việc cho bất kỳ lớp (sử dụng phản ánh chứ không phải là một câu lệnh switch):

public static object GetMemberByName<T>(T obj, string name) 
{ 
    PropertyInfo prop = typeof(T).GetProperty(name); 
    if(prop != null) 
     return prop.GetValue(obj, null); 

    throw new ArgumentException("Named property doesn't exist."); 
} 

Hoặc một phiên bản Phương pháp mở rộng (mà vẫn sẽ làm việc trên bất kỳ loại đối tượng):

public static object GetMemberByName<T>(this T obj, string name) 
{ 
    PropertyInfo prop = typeof(T).GetProperty(name); 
    if(prop != null) 
     return prop.GetValue(obj, null); 

    throw new ArgumentException("Named property doesn't exist."); 
} 

Rõ ràng có một số lỗi kiểm tra bổ sung mà bạn có thể muốn thực hiện, nhưng đây sẽ là khởi đầu.

Tôi cũng trả lại đối tượng kiểu từ các phương thức vì lý do. Điều này cho phép người gọi xử lý việc truyền giá trị tuy nhiên họ thấy phù hợp (nếu họ cần đúc).

+0

+1 Đã được trả lời theo cùng một cách như vậy đã cho bạn điểm. –

+0

Thật thú vị, phiên bản của Wallace Breza chấp nhận một đối tượng và trả về một T, trong khi phiên bản này chấp nhận một T và trả về một đối tượng. Bạn cũng có thể có một phương thức chung chung cho cả đầu vào và đầu ra. –

+0

@John M Gant: Tôi đã chọn để làm cho nó chung chung trên đối tượng trả về vì vậy không có bất kỳ sự tham gia nào khi sử dụng giá trị trả về. Tôi cũng muốn các phương pháp mở rộng để hiển thị trong intellisence cho tất cả các đối tượng. –

0

Bạn có thể muốn thử sử dụng một cái gì đó như thế này.

private static readonly Dictionary<Type, Dictionary<string, PropertyInfo>> _cache = new Dictionary<Type,Dictionary<string,PropertyInfo>>(); 
public static T GetProperty<T>(object obj, string name) 
{ 
    if (obj == null) 
    { 
     throw new ArgumentNullException("obj"); 
    } 
    else if (name == null) 
    { 
     throw new ArgumentNullException("name"); 
    } 

    lock (_cache) 
    { 
     var type = obj.GetType(); 
     var props = default(Dictionary<string, PropertyInfo>); 
     if (!_cache.TryGetValue(type, out props)) 
     { 
      props = new Dictionary<string, PropertyInfo>(); 
      _cache.Add(type, props); 
     } 
     var prop = default(PropertyInfo); 
     if (!props.TryGetValue(name, out prop)) 
     { 
      prop = type.GetProperty(name); 
      if (prop == null) 
      { 
       throw new MissingMemberException(name); 
      } 
      props.Add(name, prop); 
     } 
     return (T)prop.GetValue(obj, null); 
    } 
} 
5

Có một vài tùy chọn bạn có thể thử.

Tùy chọn 1: Để đối tượng lưu trữ giá trị thuộc tính động.

public GetMemberByName(MyObject myobj, string name) 
{ 
    return myobj.GetProperty(name); 
} 

public class MyObject 
{ 
    private Dictionary<string, object> m_Properties = new Dictionary<string, object>(); 

    public object GetProperty(string name) 
    { 
     return m_Properties[name]; 
    } 

    public void SetProperty(string name, object value) 
    { 
     m_Properties[name] = value; 
    } 

    public object Prop1 
    { 
     get { return GetProperty("PropOne"); } 
     set { SetProperty("PropOne", value); } 
    } 

    public object Prop2 
    { 
     get { return GetProperty("PropTwo"); } 
     set { SetProperty("PropTwo", value); } 
    } 
} 

Lựa chọn 2: Sử dụng phản ánh.

public GetMemberByName(MyObject myobj, string name) 
{ 
    return typeof(MyObject).GetProperty(name).GetValue(obj, null); 
} 

Lựa chọn 3: Để lại nó theo cách nó được.

Đây là tùy chọn hợp lý vì báo cáo chuyển đổi trên các loại dữ liệu chuỗi sẽ được chuyển thành tra cứu Dictionary sau khi báo cáo trường hợp số đạt đến ngưỡng nhất định. Ngưỡng đó là 7 trên trình biên dịch C# 3.0. Vì vậy, việc tra cứu sẽ là O (1) cho dù có bao nhiêu trường hợp. Nó sẽ không quét qua từng cái một.

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