2012-02-17 41 views
5

Cho đối tượng MethodDeclarationSyntax làm thế nào tôi có thể tìm ra kiểu khai báo của phương thức?Tìm kiểu khai báo của phương thức

Vấn đề thực tế của tôi là tôi cần tìm hiểu xem liệu phương pháp được tham chiếu có đang triển khai phương thức giao diện hay không.

Ví dụ, với mã dưới đây, nếu tôi có một MethodDeclarationSyntax cho phương pháp Vứt bỏ(), làm thế nào có thể kết luận nó là việc triển khai IDisposable.Dispose()?

using System; 
abstract class InterfaceImplementation : IDisposable 
{ 
    public abstract void Dispose(); 
} 

Tôi đã cố gắng nhận kiểu khai báo của phương thức (và kiểm tra loại) không thành công (Thuộc tính cha mẹ cung cấp cho tôi lớp InterfaceImplementation).

Tôi cũng đã cố gắng để lấy các biểu tượng ngữ nghĩa cho phương pháp:

var methodSymbol = (MethodSymbol) semanticModel.GetDeclaredSymbol(methodDeclaration); 

nhưng không thể nhận ra bất cứ điều gì có thể giúp tôi.

Ý tưởng?

Trả lời

7

Một khi bạn có biểu tượng phương pháp, bạn có thể hỏi nếu một phương pháp cụ thể được thực hiện một giao diện phương pháp trong một loại nhất định. Mã này khá đơn giản:

MethodSymbol method = ...; 
TypeSymbol type = method.ContainingType; 
MethodSymbol disposeMethod = (MethodSymbol)c.GetSpecialType(SpecialType.System_IDisposable).GetMembers("Dispose").Single(); 
bool isDisposeMethod = method.Equals(type.FindImplementationForInterfaceMember(disposeMethod)); 

Điều quan trọng cần lưu ý là loại có chứa phương pháp Disisposable. Trong C#, có thể cho một phương thức để thực hiện một phương thức giao diện chỉ được khai báo trên một kiểu dẫn xuất. Cụ thể hơn, nếu bạn ommtted ": IDisposable" trên mã của bạn ở trên, và đã có một loại có nguồn gốc của InterfaceImplementation được IDisposable, rằng Dispose() phương pháp vẫn có thể thực hiện nó.

+0

Tôi sẽ sử dụng toán tử '==' thay vì 'Equals()' ở đây, vì 'FindImplementationForInterfaceMember()' có thể trả về 'null'. Hoặc ít nhất là viết 'Equals()' theo cách khác. – svick

+0

@svick: điểm tốt khi hoán đổi thứ tự Bằng. Việc sử dụng Equals của tôi không phải ngẫu nhiên, như một thói quen quan trọng mà chúng tôi đã phát triển trong nhóm Roslyn: sử dụng == sẽ hoạt động tốt miễn là bạn chỉ sử dụng các loại ngôn ngữ cụ thể. Nếu bạn có hai IMethodSymbols, bạn * phải * sử dụng Equals như == không bị quá tải trong trường hợp đó. –

+0

@ Jason tôi sợ điều này sẽ không giúp tôi vì nó giả định tôi biết những phương pháp tôi cần phải kiểm tra (trong mã của bạn, bạn lấy một tham chiếu đến Dispose() phương pháp biểu tượng và so sánh với điều đó) mà không phải là trường hợp. Tất nhiên tôi có thể kiểm tra lớp cơ sở/giao diện đệ quy (cho đến khi tôi đạt được đối tượng) nhưng tôi sẽ mong đợi rằng lớp MethodSymbol có thể cung cấp cho tôi thông tin này trực tiếp. – Vagaus

4

Các loại cú pháp (như MethodDeclarationSyntax) hoạt động chỉ ở cấp cú pháp. Ở cấp độ này, không có kiến ​​thức cho dù phương pháp Dispose thực hiện IDisposable. Đó là bởi vì bạn chưa biết phương thức IDisposable có. Hơn nữa, bạn thậm chí không biết liệu IDisposable có tồn tại hay không, cho dù đó là một lớp hay giao diện hay tên đầy đủ của nó. (Có phải là System.IDisposable? Hoặc MyNamespace.IDisposable?)

Để nhận được thông tin như vậy, bạn cần đạt đến cấp ngữ nghĩa, như bạn đã đoán.

Tôi không tìm được cách nào để chuyển trực tiếp từ giao thức sang giao diện trừ khi đó là giao diện rõ ràng (EDIT: đó là vì không phải lúc nào cũng có thể, xem nhận xét của Kevin). Nhưng bạn có thể nhận được từ một loại để thực hiện một số phương thức giao diện cụ thể.

Vì vậy, nếu bạn muốn tìm ra rằng một số MethodSymbol thực hiện IDisposable.Dispose(), bạn có thể làm một cái gì đó như:

SyntaxTree unit = SyntaxTree.ParseCompilationUnit(code); 

MethodDeclarationSyntax method = …; 

var compilation = Compilation.Create("test") 
    .AddReferences(new AssemblyFileReference(typeof(object).Assembly.Location)) 
    .AddSyntaxTrees(unit); 

SemanticModel model = compilation.GetSemanticModel(unit); 

MethodSymbol methodSymbol = (MethodSymbol)model.GetDeclaredSymbol(method); 

var typeSymbol = methodSymbol.ContainingType; 

var idisposableDisposeSymbol = model.BindExpression(
    0, Syntax.ParseExpression("System.IDisposable.Dispose()")).Symbol; 

var implementation = typeSymbol.FindImplementationForInterfaceMember(
    idisposableDisposeSymbol); 

bool methodImplementsDispose = methodSymbol == implementation; 
+1

Lý do bạn không thể làm điều này từ phương pháp này là đôi khi bạn không thể nói. Nếu bạn có lớp 'lớp cơ sở {public void Dispose()} Có nguồn gốc: Base, IDisposable {}' thì "Dispose" là việc thực hiện _if bạn có một cá thể của Derived_, nhưng không phải nếu bạn có một cá thể của Base ... –

+0

Hmm, tôi không nhận ra điều đó thậm chí có thể, thú vị. – svick

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