2013-08-12 33 views
5

Tôi có một bộ sưu tập lớn các đối tượng được tạo tự động. Mặc dù chúng là tất cả các lớp khác nhau, không liên quan, tất cả các đối tượng đều chia sẻ một số thuộc tính cơ bản (tên, id, v.v.). Tôi không kiểm soát việc tạo các đối tượng này, vì vậy rất tiếc tôi không thể thực hiện phương pháp lý tưởng để triển khai giao diện. Tôi muốn tạo ra một phương pháp mà tôi vượt qua một tùy ý của các đối tượng này và làm một cái gì đó bằng cách sử dụng các thuộc tính phổ biến này.Phương pháp xử lý các đối tượng có thuộc tính chung, nhưng các loại đối tượng khác nhau

Ý tưởng chung sẽ là một cái gì đó như:

someObj a = new someObj(); 
a.name = "sara"; 
diffObj b = new diffObj(); 
b.name = "joe"; 
string phrase = string.Format("I am with {0} and {1}", 
    getName(a), getName(b)); 

private string getName(object anyObjWithName) 
{ 
    return anyObjWithName.name; 
} 

mặc dù tự nhiên này không hoạt động.

Tôi nghĩ rằng một phương pháp chung có thể giữ câu trả lời, nhưng cách duy nhất tôi có thể thấy để gọi nó với loại hiện tại đang sử dụng genericMethod.Invoke, vẫn mang cùng một vấn đề không thể giải quyết các thuộc tính của đối tượng được truyền trong phương pháp. Điều này không giống như Calling generic method with a type argument known only at execution time hoặc How to call generic method with a given Type object? trong đó chỉ có loại hoặc thuộc tính của loại, được sử dụng trong phương pháp, trái với thuộc tính của đối tượng.

Tôi biết rằng điều này sẽ (rất) dễ bị lỗi, nhưng tôi có thể đảm bảo rằng tất cả các đối tượng gặp phải sẽ có các thuộc tính chung được thao tác.

+5

Sử dụng giao diện? – Michael

+0

Hãy thử khai báo một giao diện để liên kết các đối tượng cho các thuộc tính chung hoặc sử dụng sự phản chiếu. – Nayan

+1

http://msdn.microsoft.com/en-us/library/64syzecx.aspx – Crisfole

Trả lời

9

tôi có thể đảm bảo rằng tất cả các đối tượng gặp phải sẽ có tài sản chung đã được chế tác

Nếu đó là trường hợp, bạn có thể sử dụng dynamic:

private string getName(dynamic anyObjWithName) 
{ 
    return anyObjWithName.name; 
} 

Hãy nhận biết rằng việc sử dụng bất kỳ đối tượng đó không có thuộc tính name sẽ không bị lỗi cho đến khi chạy.

Nếu bạn muốn thêm một chút về an toàn, bạn có thể bắt RuntimeBinderException đó được ném nếu tài sản không tồn tại:

private string getName(dynamic anyObjWithName) 
{ 
    try { 
     return anyObjWithName.name; 
    } 
    catch(RuntimeBinderException) { 
     return "{unknown}"; 
    } 
} 
+0

Tuyệt, tôi chưa bao giờ thấy giải pháp này ... – ganders

+3

Với $ 0,02, tôi không nghĩ rằng OP ở vị trí bắt đầu đường dẫn 'động'. Con đường đầy nguy hiểm và câu trả lời này không phải là sư phạm. –

+1

Tôi biết tôi đã ở trong một số cơn đau đi vào nó. Vì tôi không thể triển khai giao diện, tôi không chắc chắn có một tùy chọn khác. – MaxPRafferty

0

Bạn đang tạo một thiết bị Rube Goldberg ở đó. Bạn chỉ nên có tất cả các lớp đối tượng dữ liệu của bạn thực hiện một giao diện duy nhất, sau đó bạn có thể làm việc trên đó. Đơn giản hơn và ít lỗi hơn là không quan trọng với sự phản chiếu.

Thực tế là rất nhiều đối tượng có thuộc tính chung nhưng không chia sẻ cùng một tổ tiên, trên ít nhất một giao diện chung, cho thấy có điều gì đó sai với thiết kế của bạn. Hãy suy nghĩ lại.

+2

Đó thực sự là vấn đề xuất hiện - Tôi không kiểm soát việc tạo ra các vật thể. Vương quốc của tôi cho một giao diện! – MaxPRafferty

0

Nhiều cách để thực hiện điều này, đơn giản nhất có lẽ là để tạo ra giao diện và tuyên bố phương pháp phổ biến ở đó, có đối tượng của bạn thực hiện nó, sau đó thay đổi "getName" phương pháp mất giao diện đối tượng

private string getName(IMyInterface anyObjWithName) 
{ 
    return anyObjWithName.name; 
} 
0

Cách đúng để làm điều này có giao diện, nếu bạn sở hữu các loại mà bạn đang làm việc với

public interface IEntity 
{ 
    int ID { get; set; } 
    string Name { get; set; } 
} 

public class TypeOne : IEntity 
{ 
    public int ID { get; set; } 
    public string Name { get; set } 

    public string BespokePropertyOne { get; set;} 
} 

public class TypeTwo : IEntity 
{ 
    public int ID { get; set; } 
    public string Name { get; set; } 

    public float BespokePropertyTwo { get; set; } 
} 

static void Main(string[] args) 
{ 
    List<IEntity> entities = new List<IEntity>(); 
    entities.Add(new TypeOne() { ID = 1, Name = "Bob", BespokePropertyOne = "blablabla" }); 
    entities.Add(new TypeTwo() { ID = 2, Name = "Alice", BespokePropertyTwo = 5.4f }); 

    foreach (IEntity entity in entities) 
    { 
     Console.WriteLine("ID: {0} Name: {1}", entity.ID, entity.Name); 
    } 
} 
+0

OP doesnt có một cách để kiểm soát các lớp học. Nó được đề cập trong câu hỏi. – nawfal

0

Câu trả lời này cho biết rằng giao diện không thể thực hiện được trong trường hợp này. Có lẽ nó có thể giúp người khác đọc câu hỏi này.

Interface:

interface Iname 
{ 
    string Name { get; set; } 
} 

Sử dụng giao diện:

class A : Iname 
{ 
    public string Name { get; set; } 
} 

class B : Iname 
{ 
    public string Name { get; set; } 
} 

Phương pháp:

string GetName(Iname o) 
{ 
    return o.Name; 
} 

Sử dụng:

A a = new A { Name = "First" }; 
B b = new B { Name = "Last" }; 
Text = GetName(a) + " " + GetName(b); 
+0

OP doesnt có một cách để kiểm soát các lớp học. Nó được đề cập trong câu hỏi. – nawfal

2

Nếu bạn không hài lòng với hiệu suất sử dụng năng động như đã đề cập bởi D Stanley, bạn luôn có thể thử FastMember.

Tất cả những gì bạn cần biết để bắt đầu sử dụng nó được hiển thị khá nhiều trong ví dụ 2 mã đầu tiên.

+0

Ồ, vâng, thư viện này theo nghĩa đen tồn tại để giải quyết vấn đề chính xác này. Tôi tự hỏi nó như thế nào dưới mui xe mà không phản ánh ... – MaxPRafferty

+1

@MaxPRafferty: IIRC, nó sẽ tận dụng sự phản ánh để lấy thông tin thành viên lần đầu tiên, viết thời gian biên dịch tương đương IL, sau đó sử dụng mã đã biên dịch đó để truy cập loại/thành viên. Điều này cho nó tốc độ gần hơn với mã được biên dịch khi so sánh với việc thường xuyên đánh DLR hoặc sự phản chiếu. –

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