2009-03-18 38 views
14

Bất kể đây có phải là một ý tưởng tốt hay không, có thể triển khai giao diện trong đó chức năng thực hiện nhận biết kiểu của đối tượng gọi không?Xác định loại đối tượng gọi trong C#

class A 
{ 
    private C; 

    public int doC(int input) 
    { 
     return C.DoSomething(input); 
    } 
} 

class B 
{ 
    private C; 

    public int doC(int input) 
    { 
     return C.DoSomething(input); 
    } 
} 

class C 
{ 
    public int DoSomething(int input) 
    { 
     if(GetType(CallingObject) == A) 
     { 
     return input + 1; 
     } 
     else if(GetType(CallingObject) == B) 
     { 
     return input + 2; 
     } 
     else 
     { 
     return input + 3; 
     } 
    } 
} 

Dường như với tôi như thế này là một thực hành mã hóa xấu (vì các thông số không thay đổi, nhưng đầu ra sẽ) nhưng ngoài việc đó là có thể?

Tôi đang trong một tình huống là tôi muốn có một vài loại cụ thể để có thể gọi một chức năng nhất định, nhưng tôi không thể loại trừ quyền truy cập vào chức năng. Tôi nghĩ về việc có một "loại" tham số

DoSomething(int input, Type callingobject) 

Nhưng không có đảm bảo rằng đối tượng gọi điện thoại sẽ sử dụng GetType (này), như trái ngược với GetType (B) để spoof một B bất kể loại riêng của họ.

Điều này có đơn giản (tương đối đơn giản) khi kiểm tra lồng giặt không?


EDIT

hãy bỏ phiếu lên câu trả lời JaredPar (nếu bạn muốn) cũng như John Feminella của. Tôi không thể đánh dấu cả hai câu trả lời như được chấp nhận, và tôi chấp nhận câu trả lời của John Feminella bởi vì nó đặc biệt là những gì tôi yêu cầu, trái ngược với câu trả lời của Jared đã đưa ra một giải pháp mà trước đây tôi chưa từng xem xét.

+0

thể bạn cho một chút nền hơn? Nguyên nhân bây giờ tôi không thấy lý do tại sao bạn không chỉ thực hiện các phương pháp riêng biệt trong C. Một cái gì đó như C.DoSometingA và C.DoSomethingB. Hoặc thậm chí tốt hơn: nhưng hành vi trong các lớp học A & B. –

+1

Phiên bản ngắn là tôi sẽ có 30 hoặc 40 chức năng giống hệt nhau chỉ đơn giản là tắt một hoặc hai dòng. – DevinB

+0

@devinb: Xem chỉnh sửa tôi đã thực hiện cho bài đăng của mình bên dưới. Bạn có thể cân nhắc việc mở một câu hỏi khác. –

Trả lời

17

Trước tiên, có, đó là một ý tưởng khủng khiếp để làm điều này và phá vỡ tất cả các loại nguyên tắc thiết kế vững chắc. Bạn chắc chắn nên xem xét một cách tiếp cận thay thế nếu đó là mở, giống như chỉ đơn giản bằng cách sử dụng đa hình - điều này có vẻ như nó có thể được refactored đến một trường hợp khá rõ ràng của công văn duy nhất.

Thứ hai, có, có thể. Sử dụng System.Diagnostics.StackTrace để đi bộ ngăn xếp; sau đó nhận được StackFrame một cấp phù hợp. Sau đó, xác định phương thức nào là người gọi bằng cách sử dụng GetMethod() trên số đó StackFrame. Lưu ý rằng việc tạo dấu vết ngăn xếp là một hoạt động có khả năng tốn kém và có thể cho người gọi phương pháp của bạn che khuất nơi mọi thứ thực sự đến từ đó.


Edit: Nhận xét này từ OP làm cho nó khá rõ ràng rằng điều này có lẽ có thể là một phương pháp chung chung hoặc polymorphic. @devinb, bạn có thể muốn cân nhắc tạo câu hỏi mới cung cấp thêm chi tiết về những gì bạn đang cố gắng làm và chúng tôi có thể xem liệu nó có thể tự vay tốt với giải pháp tốt hay không.

Phiên bản ngắn là tôi sẽ kết thúc có 30 hoặc 40 chức năng giống hệt nhau là chỉ đơn giản là tắt bởi một hoặc hai dòng. - devinb (12 giây trước)

+0

D'oh! Đang xóa "câu trả lời trùng lặp" của tôi. . . những ngón tay chậm chạp ngu ngốc. . . +1 BTW –

+1

Không có mồ hôi - Tôi nhận được ninja tất cả các thời gian bởi đó dratted Jon Skeet. BTW, tôi sẽ không xóa bài viết của bạn trừ khi nó khá gần với một bản sao chính xác. Nếu bạn thực hiện một điểm mà chủ yếu là tương tự như tôi nhưng khác nhau trong một số cách, chắc chắn giữ cho xung quanh. –

0

Có (hầu hết) luôn là thiết kế phù hợp có thể thực hiện những gì bạn cần. Nếu bạn lùi lại một bước để mô tả những gì bạn thực sự cần phải làm, tôi tin rằng bạn sẽ có được ít nhất một thiết kế tốt mà không yêu cầu bạn phải nghỉ mát đến một cái gì đó như thế này.

+0

Thiết kế của tôi là một cơn ác mộng kết hợp rất chặt chẽ và tự tham khảo, vì vậy có, tôi đã nghĩ ra những thiết kế không có sự xấu xí đặc biệt này, nhưng chúng có sự xấu xa của riêng chúng. Tôi chỉ đang cố gắng khám phá từng tùy chọn. – DevinB

+0

@devinb tôi thấy không chắc rằng các tùy chọn khác cũng xấu xí như thế này –

+0

Chúng phức tạp hơn. Trái với cái này tương đối đơn giản, nhưng khủng khiếp. = D Tôi hầu như chỉ tò mò. – DevinB

13

Là một phương pháp thay thế, bạn đã bao giờ xem xét việc cung cấp một lớp khác dựa trên loại đối tượng yêu cầu lớp học.Nói như sau

public interface IC { 
    int DoSomething(); 
} 

public static CFactory { 
    public IC GetC(Type requestingType) { 
    if (requestingType == typeof(BadType1)) { 
     return new CForBadType1(); 
    } else if (requestingType == typeof(OtherType) { 
     return new CForOtherType(); 
    } 
    ... 
    } 
} 

Đây sẽ là cách tiếp cận sạch hơn nhiều so với mỗi phương pháp thay đổi hành vi dựa trên đối tượng gọi. Nó sẽ làm sạch một cách rõ ràng các mối quan tâm đối với việc triển khai IC khác nhau. Ngoài ra, họ có thể tất cả các proxy trở lại thực hiện C thực.

EDIT Kiểm tra callstack

Theo một số người khác đã chỉ ra bạn có thể kiểm tra callstack để xác định những đối tượng được ngay lập tức gọi hàm. Tuy nhiên đây không phải là một cách dễ dàng để xác định xem một trong những đối tượng bạn muốn trường hợp đặc biệt đang gọi bạn. Ví dụ tôi có thể làm như sau để gọi cho bạn từ SomeBadObject nhưng làm cho nó rất khó khăn cho bạn để xác định rằng tôi đã làm như vậy.

public class SomeBadObject { 
    public void CallCIndirectly(C obj) { 
    var ret = Helper.CallDoSomething(c); 
    } 
} 

public static class Helper { 
    public int CallDoSomething(C obj) { 
    return obj.DoSomething(); 
    } 
} 

Tất nhiên, bạn có thể đi xa hơn về ngăn xếp cuộc gọi. Nhưng điều đó thậm chí còn mong manh hơn bởi vì nó có thể là một con đường hoàn toàn hợp pháp để SomeBadObject nằm trên stack khi một đối tượng khác gọi là DoSomething.

+0

Điều này vẫn yêu cầu A và B để cung cấp loại của họ cho CFactory? Làm cách nào để ngăn chặn giả mạo? – DevinB

+0

@devinb, bạn có thể đi bộ ngăn xếp để ngăn chặn giả mạo nhưng nó không phải là một cách dễ dàng. .Net không được thiết kế cho một chức năng để biết đó là người gọi và kết quả là bất kỳ nỗ lực nào để nó hoạt động sẽ có lỗ hổng. – JaredPar

+0

Tôi đang cố gắng để mã hóa với mô hình mà một coder độc hại sẽ có quyền truy cập vào nguồn của tôi, nhưng không thể thay đổi nó. Nhưng tôi nghĩ rằng bạn đang ở trong đó gần như không thể loại trừ tất cả các khả năng "badObject" – DevinB

0

Vâng, bạn có thể thử lấy dấu vết ngăn xếp và xác định loại người gọi từ đó, đó là một overkill theo ý kiến ​​của tôi và sẽ được làm chậm.

Giao diện A,B sẽ triển khai như thế nào?

interface IFoo { 
    int Value { get; } 
} 

Và sau đó phương pháp DoSomething của bạn sẽ trông như thế này:

public int DoSomething(int input, IFoo foo) 
    { 
     return input + foo.Value; 
    } 
0

Bạn có thể sử dụng System.Diagnostics.StackTrace lớp để tạo ra một vết đống. Sau đó, bạn có thể tìm kiếm StackFrame được liên kết với người gọi. StackFrame có thuộc tính Phương thức mà bạn có thể sử dụng để truy cập loại người gọi.

Tuy nhiên, phương pháp trên không có gì nên được sử dụng trong mã quan trọng hiệu suất.

0

Bạn có thể kiểm tra ngăn xếp cuộc gọi, nhưng điều đó vừa tốn kém vừa dễ vỡ. Khi mã của bạn được jit'ed trình biên dịch có thể inline phương pháp của bạn vì vậy trong khi nó có thể làm việc trong chế độ gỡ lỗi bạn có thể nhận được một ngăn xếp khác nhau khi biên dịch trong chế độ phát hành.

2

Câu trả lời đơn giản nhất là chuyển đối tượng người gửi giống như bất kỳ sự kiện nào với người gửi điển hình, phương pháp eventargs.

mã gọi của bạn sẽ trông như thế này:

return c.DoSomething(input, this); 

phương pháp DoSomething của bạn chỉ đơn giản là sẽ kiểm tra các loại sử dụng toán tử IS:

public static int DoSomething(int input, object source) 
{ 
    if(source is A) 
     return input + 1; 
    else if(source is B) 
     return input + 2; 
    else 
     throw new ApplicationException(); 

} 

Điều này có vẻ giống như một cái gì đó với một chút OOP hơn. Bạn có thể xem xét C một lớp trừu tượng với một phương thức, và có A, B kế thừa từ C và chỉ cần gọi phương thức. Điều này sẽ cho phép bạn kiểm tra loại đối tượng cơ sở, mà không rõ ràng là giả mạo.

Ngoài sự tò mò, bạn đang cố gắng gì với cấu trúc này?

+0

Nói một cách tổng quát, C chứa thông tin cho A và thông tin cho B và phương thức truy cập thông tin đó giống hệt nhau, nhưng B không thể truy cập (hoặc truy cập ABLE) Thông tin và B không thể truy cập thông tin. ... gần. – DevinB

+1

Chà, điều đó làm tôi bối rối hơn: P Bạn có thể biến nó thành một ví dụ thế giới thực? Giống như .. ví dụ về mèo và chó hay gì đó? –

+1

Hmm ... có thể. Cơ sở dữ liệu chứa danh sách các điệp viên Mỹ và gián điệp Nga. Phương thức trả về một danh sách. Khi người Mỹ hỏi, họ nhận được danh sách người Mỹ, khi người Nga hỏi, họ nhận được danh sách của Nga. – DevinB

0

cấu trúc nó giống như một xử lý sự kiện, tôi chắc chắn rằng cảnh sát FX thậm chí sẽ đề nghị bạn làm điều này

static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) 
     { 
      throw new NotImplementedException(); 
     } 
+0

... Tôi không hiểu. – DevinB

+0

Tôi đang sử dụng những gì Matt Murrell đề nghị, nhưng ít nhất sử dụng cùng một quy ước như các sự kiện được trình bày. tức là vượt qua đối tượng gọi của bạn, sau đó bạn thậm chí còn lập luận – dfasdljkhfaskldjhfasklhf

2

Không đáng tin cậy do khả năng nội tuyến bởi thời gian chạy.

+0

Giả sử các hàm này quá lớn để nội tuyến. – DevinB

0

tôi có thể gặp rắc rối nghĩ tôi sẽ xử lý việc này khác nhau nhưng ....

Tôi giả định này:

lớp Một kêu gọi các lớp E để biết thông tin
cuộc gọi lớp C trên lớp E để biết thông tin
cả hai thông qua cùng một phương pháp trong E

Bạn biết và tạo cả ba lớp và có thể ẩn nội dung của lớp E ra công chúng. Nếu đây không phải là trường hợp, kiểm soát của bạn sẽ luôn luôn bị mất.

Hợp lý: nếu bạn có thể ẩn nội dung của lớp E, bạn làm điều này rất có thể bằng cách phân phối thông qua một DLL.

Nếu bạn làm điều này dù sao, tại sao không che giấu nội dung của lớp A và C cũng như (cùng phương pháp) nhưng cho phép A và C sẽ được sử dụng làm cơ sở cho nguồn gốc lớp B và D.

Trong lớp Một và C bạn sẽ có một phương thức để gọi phương thức trong lớp E và phân phối kết quả. Trong phương thức đó (có thể sử dụng bởi lớp dẫn xuất, nhưng nội dung được ẩn với người dùng), bạn có thể có chuỗi dài là "khóa" được chuyển đến phương thức trong lớp E. Những chuỗi này không thể đoán được bởi bất kỳ người dùng nào và sẽ chỉ được biết để các lớp cơ sở A, C và E.

có các lớp cơ sở đóng gói các kiến ​​thức về làm thế nào để gọi phương thức trong E một cách chính xác sẽ được thực hiện hoàn hảo OOP tôi nghĩ

Sau đó, một lần nữa ... tôi có thể nhìn sự phức tạp ở đây.

4

Bắt đầu với Visual Studio 2012 (Khuôn khổ .NET 4.5), bạn có thể tự động chuyển caller information cho một phương thức bằng cách sử dụng thuộc tính người gọi (VB và C#).

public void TheCaller() 
{ 
    SomeMethod(); 
} 

public void SomeMethod([CallerMemberName] string memberName = "") 
{ 
    Console.WriteLine(memberName); // ==> "TheCaller" 
} 

Thuộc tính người gọi nằm trong không gian tên System.Runtime.CompilerServices.


này là lý tưởng cho việc thực hiện các INotifyPropertyChanged:

private void OnPropertyChanged([CallerMemberName]string caller = null) { 
    var handler = PropertyChanged; 
    if (handler != null) { 
     handler(this, new PropertyChangedEventArgs(caller)); 
    } 
} 

Ví dụ:

private string _name; 
public string Name 
{ 
    get { return _name; } 
    set 
    { 
     if (value != _name) { 
      _name = value; 
      OnPropertyChanged(); // Call without the optional parameter! 
     } 
    } 
} 
+0

+1 để chỉ ra điều này, vì mục đích thêm thuộc tính thông tin người gọi trong 4.5 là một ví dụ tuyệt vời về ngữ cảnh trong đó thực tế là cần thiết, ngay cả khi nó có vẻ thoáng qua xuất hiện vi phạm nguyên tắc thiết kế tốt. Cụ thể, khi viết công cụ dò tìm, gỡ lỗi hoặc chẩn đoán. – BitMask777

+1

@ BitMask777: Có, một ví dụ khác có ý nghĩa khi triển khai 'INotifyPropertyChanged'. –

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