2009-05-05 28 views
46

Tôi đang cố tạo một phương thức sẽ đi qua danh sách các đối tượng chung và thay thế tất cả các thuộc tính của chúng thuộc loại string, hoặc là null hoặc trống với một sự thay thế.C#: Cách nhận tất cả các thuộc tính chuỗi công khai (cả nhận và đặt) của một loại

Làm cách nào để thực hiện điều này?

tôi có loại ... vỏ ... cho đến nay:

public static void ReplaceEmptyStrings<T>(List<T> list, string replacement) 
{ 
    var properties = typeof(T).GetProperties(-- What BindingFlags? --); 

    foreach(var p in properties) 
    { 
     foreach(var item in list) 
     { 
      if(string.IsNullOrEmpty((string) p.GetValue(item, null))) 
       p.SetValue(item, replacement, null); 
     } 
    } 
} 

Vì vậy, làm thế nào tôi tìm thấy tất cả các tính chất của một loại đó là:

  • Trong số loại string
  • Có công get
  • Có công set

    ?


tôi làm lớp thử nghiệm này:

class TestSubject 
{ 
    public string Public; 
    private string Private; 

    public string PublicPublic { get; set; } 
    public string PublicPrivate { get; private set; } 
    public string PrivatePublic { private get; set; } 
    private string PrivatePrivate { get; set; } 
} 

Sau đây không hoạt động:

var properties = typeof(TestSubject) 
     .GetProperties(BindingFlags.Instance|BindingFlags.Public) 
     .Where(ø => ø.CanRead && ø.CanWrite) 
     .Where(ø => ø.PropertyType == typeof(string)); 

Nếu tôi in ra tên của những tài sản tôi đạt được điều đó, tôi get:

PublicPublic PublicPrivate PrivatePublic

Nói cách khác, tôi nhận được hai thuộc tính quá nhiều.


Note: Đây có lẽ có thể được thực hiện một cách tốt nhất ... sử dụng foreach lồng nhau và phản ánh và tất cả đây ... nhưng nếu bạn có bất kỳ ý tưởng thay thế tuyệt vời, xin vui lòng cho tôi biết vì tôi muốn học!

Trả lời

79

Viết mã của bạn. Không sử dụng LINQ hay var.

public static void ReplaceEmptyStrings<T>(List<T> list, string replacement) 
{ 
    PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); 

    foreach (PropertyInfo p in properties) 
    { 
     // Only work with strings 
     if (p.PropertyType != typeof(string)) { continue; } 

     // If not writable then cannot null it; if not readable then cannot check it's value 
     if (!p.CanWrite || !p.CanRead) { continue; } 

     MethodInfo mget = p.GetGetMethod(false); 
     MethodInfo mset = p.GetSetMethod(false); 

     // Get and set methods have to be public 
     if (mget == null) { continue; } 
     if (mset == null) { continue; } 

     foreach (T item in list) 
     { 
      if (string.IsNullOrEmpty((string)p.GetValue(item, null))) 
      { 
       p.SetValue(item, replacement, null); 
      } 
     } 
    } 
} 
+0

Ví dụ của bạn trước hết sẽ thay thế * tất cả * giá trị, và thứ hai, thuộc tính CanWrite dường như không hoạt động theo cách chúng ta nghĩ ... =/ – Svish

+0

CanWrite thực sự hoạt động theo cách cần thiết. Bạn có thể giải thích điều gì khiến bạn nghĩ ngược lại không? –

+0

Như ví dụ của tôi cho thấy, một thuộc tính công khai được khai báo là tức làchuỗi công khai Something {get; private set;}, sẽ trả về true cho cả CanRead và CanWrite, mặc dù tôi không thể viết, vì setter là private. – Svish

0

BindingFlags.Public | BindingFlags.Instance nên làm điều đó

GetSetMethod()

9

Bạn sẽ tìm thấy các thuộc tính như vậy với BindingFlags.Public | BindingFlags.Instance. Sau đó, bạn sẽ cần phải kiểm tra từng cá thể PropertyInfo bằng cách kiểm tra các thuộc tính CanWrite và CanRead, để tìm hiểu xem chúng có thể đọc và/hoặc ghi được hay không.

Cập nhật: mã ví dụ

PropertyInfo[] props = yourClassInstance.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); 
for (int i = 0; i < props.Length; i++) 
{ 
    if (props[i].PropertyType == typeof(string) && props[i].CanWrite) 
    { 
     // do your update 
    } 
} 

Tôi nhìn vào nó chi tiết hơn sau khi cập nhật của bạn.Nếu bạn cũng kiểm tra các đối tượng MethodInfo được trả về bởi GetGetMethod và GetSetMethod, bạn sẽ đạt được mục tiêu, tôi nghĩ;

var properties = typeof(TestSubject).GetProperties(BindingFlags.Instance | BindingFlags.Public) 
     .Where(ø => ø.CanRead && ø.CanWrite) 
     .Where(ø => ø.PropertyType == typeof(string)) 
     .Where(ø => ø.GetGetMethod(true).IsPublic) 
     .Where(ø => ø.GetSetMethod(true).IsPublic); 

Theo mặc định hai phương pháp này trở lại chỉ thu khí công và setters (mạo hiểm một NullReferenceException trong một trường hợp như thế này), nhưng đi qua true như trên khiến họ cũng trở lại loại hình tư thục. Sau đó, bạn có thể kiểm tra các thuộc tính IsPublic (hoặc IsPrivate).

+0

Điều này không hiệu quả. Xem ví dụ của tôi. Mặc dù nhận được hoặc đặt là riêng tư, nó nói rằng nó có thể vừa đọc vừa viết, miễn là một trong số đó là công khai. – Svish

0

Tôi đề xuất một cách tiếp cận khác: AOP.
Bạn có thể chặn thiết lập và đặt giá trị mong muốn thành giá trị hợp lệ. Với PostSharp nó khá dễ dàng.

+0

Huh? Làm sao? – Svish

+0

Như tôi đã nói, đó là một cách tiếp cận khác. Thay vì thay đổi giá trị * sau khi * nó được đặt, tôi đề nghị chặn thiết lập, và thay đổi giá trị nếu cần thiết. Khi sử dụng PostSharp, bạn có thể viết các thuộc tính và sử dụng chúng với các thuộc tính của bạn. –

+0

bạn sẽ chặn một setter như thế nào? bạn sẽ không cần phải có quyền truy cập vào các lớp hoặc ghi đè lên tài sản hoặc một cái gì đó sau đó? – Svish

1

Nếu bạn không chỉ định bất kỳ cờ ràng buộc nào, bạn sẽ nhận được các thuộc tính công khai, ví dụ - đó là những gì bạn muốn. Nhưng sau đó bạn sẽ cần phải kiểm tra xem PropertyType trên đối tượng PropertyInfo có thuộc kiểu String hay không. Trừ khi bạn biết trước, bạn cũng sẽ cần kiểm tra xem thuộc tính có thể đọc/ghi được không vì @Fredrik cho biết.

using System.Linq; 

public static void ReplaceEmptyStrings<T>(List<T> list, string replacement) 
{ 
    var properties = typeof(T).GetProperties() 
           .Where(p => p.PropertyType == typeof(string)); 
    foreach(var p in properties) 
    { 
     foreach(var item in list) 
     { 
      if(string.IsNullOrEmpty((string) p.GetValue(item, null))) 
       p.SetValue(item, replacement, null); 
     } 
    } 
} 
0

Tôi đồng ý với câu trả lời khác, nhưng tôi thích để cấu trúc lại việc tìm kiếm bản thân để được easly truy vấn với LINQ, vì vậy các truy vấn có thể là như sau:

 var asm = Assembly.GetExecutingAssembly(); 
     var properties = (from prop 
           in asm.GetType() 
           .GetProperties(BindingFlags.Public | BindingFlags.Instance) 
          where 
          prop.PropertyType == typeof (string) && 
          prop.CanWrite && 
          prop.CanRead 
          select prop).ToList(); 
     properties.ForEach(p => Debug.WriteLine(p.Name)); 

tôi lấy ví dụ của tôi hội loại, mà đã không đọc/ghi thuộc tính chuỗi, nhưng nếu việc tìm kiếm cùng một mã chỉ đọc tài sản, kết quả sẽ là:

  • codebase
  • Esc apedCodeBase
  • FullName
  • Location
  • ImageRuntimeVersion

Đó là những chuỗiread-only hội thuộc loại

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