2012-03-01 17 views
29

Tôi đã được mã hóa giống như sau:Tiếp cận thông tin thuộc tính từ DTE

[Attrib(typeof(MyCustomType))] 
public class TargetType 
{ 
    // ..... 
} 

Tôi muốn sử dụng EnvDTE để có được một tham chiếu đến CodeElement tham chiếu bởi typeof. Tôi biết cách lấy tham chiếu đến đối số thuộc tính và tôi có thể sử dụng Value, nhưng điều đó cho tôi chuỗi typeof(MyCustomType).

Nếu tôi sử dụng Value, tôi phải chia nhỏ chuỗi và sau đó thử tìm loại, sẽ bị lông nếu có hai loại có cùng tên nhưng không gian tên khác nhau.

Có cách nào dễ dàng hơn để thực hiện việc này không?

+4

vì vậy, làm thế nào để bạn có được tham chiếu đến đối số thuộc tính? – Maslow

+3

Bạn đã xem xét Roslyn chưa? Nó sẽ cung cấp các tính năng bạn đang tìm kiếm. – jessehouwing

+0

Bạn đã kiểm tra thuộc tính FullName của thuộc tính chưa? –

Trả lời

3

Có một cách dễ dàng hơn để làm điều này?

Không, tôi không nghĩ như vậy, ít nhất cho một < = VS2013, có vẻ như rằng CodeAttributeArgument không đi xa hơn, đó là sự xấu hổ. Họ nên tôi đã phát hành CodeAttributeArgument2Value như CodeExpr: \ ..

Nếu bạn sử dụng> = VS2014, bạn có thể nhận được quyền truy cập vào Roslyn, và nó nêntrở thành dễ dàng hơn - không biết chính xác làm thế nào bạn có thể truy cập roslyn bên trong phần mở rộng VS, phải chờ xem.

Để có được thuộc tính, bạn có thể sử dụng VS helper:

public List<CodeElement> GetAllCodeElementsOfType(
    CodeElements elements, 
    vsCMElement elementType, 

    bool includeExternalTypes) 
{ 
    var ret = new List<CodeElement>(); 

    foreach (CodeElement elem in elements) 
    { 
     // iterate all namespaces (even if they are external) 
     // > they might contain project code 
     if (elem.Kind == vsCMElement.vsCMElementNamespace) 
     { 
      ret.AddRange(
       GetAllCodeElementsOfType(
        ((CodeNamespace)elem).Members, 
        elementType, 
        includeExternalTypes)); 
     } 

     // if its not a namespace but external 
     // > ignore it 
     else if (elem.InfoLocation == vsCMInfoLocation.vsCMInfoLocationExternal && !includeExternalTypes) 
      continue; 

     // if its from the project 
     // > check its members 
     else if (elem.IsCodeType) 
     { 
      ret.AddRange(
       GetAllCodeElementsOfType(
        ((CodeType)elem).Members, 
        elementType, 
        includeExternalTypes)); 
     } 

     if (elem.Kind == elementType) 
      ret.Add(elem); 
    } 
    return ret; 
} 

Nguồn gốc: https://github.com/PombeirP/T4Factories/blob/master/T4Factories.Testbed/CodeTemplates/VisualStudioAutomationHelper.ttinclude

Trong khi đó, bạn có thể sử dụng giải pháp thụt lùi, điều này không phải là tốt đẹp, nhưng nó phải làm việc , chưa thử nghiệm chính xác 100%. Ý tưởng cơ bản là bắt đầu theo dõi ngược từ lớp và theo dõi các không gian tên/cách sử dụng khác nhau trong đường dẫn của một lớp. Này sẽ cố gắng để mô phỏng khá nhiều những gì một trình biên dịch thực sẽ làm gì, nếu nó sẽ giải quyết một kiểu:

var solution = (Solution2) _applicationObject.Solution; 
var projects = solution.Projects; 
var activeProject = projects 
    .OfType<Project>() 
    .First(); 

// locate my class. 
var myClass = GetAllCodeElementsOfType(
    activeProject.CodeModel.CodeElements, 
    vsCMElement.vsCMElementClass, false) 
    .OfType<CodeClass2>() 
    .First(x => x.Name == "Program"); 

// locate my attribute on class. 
var mySpecialAttrib = myClass 
    .Attributes 
    .OfType<CodeAttribute2>() 
    .First(); 



var attributeArgument = mySpecialAttrib.Arguments 
    .OfType<CodeAttributeArgument>() 
    .First(); 

string myType = Regex.Replace(
    attributeArgument.Value, // typeof(MyType) 
    "^typeof.*\\((.*)\\)$", "$1"); // MyType*/ 

var codeNamespace = myClass.Namespace; 
var classNamespaces = new List<string>(); 

while (codeNamespace != null) 
{ 
    var codeNs = codeNamespace; 
    var namespaceName = codeNs.FullName; 

    var foundNamespaces = new List<string> {namespaceName}; 

    // generate namespaces from usings. 
    var @usings = codeNs.Children 
     .OfType<CodeImport>() 
     .Select(x => 
      new[] 
      { 
       x.Namespace, 
       namespaceName + "." + x.Namespace 
      }) 
     .SelectMany(x => x) 
     .ToList(); 

    foundNamespaces.AddRange(@usings); 

    // prepend all namespaces: 
    var extra = (
     from ns2 in classNamespaces 
     from ns1 in @usings 
     select ns1 + "." + ns2) 
     .ToList(); 

    classNamespaces.AddRange(foundNamespaces); 
    classNamespaces.AddRange(extra); 

    codeNamespace = codeNs.Parent as CodeNamespace; 
    if (codeNamespace == null) 
    { 
     var codeModel = codeNs.Parent as FileCodeModel2; 
     if (codeModel == null) return; 

     var elems = codeModel.CodeElements; 
     if (elems == null) continue; 

     var @extraUsings = elems 
      .OfType<CodeImport>() 
      .Select(x => x.Namespace); 

     classNamespaces.AddRange(@extraUsings); 
    } 
} 

// resolve to a type! 
var typeLocator = new EnvDTETypeLocator(); 
var resolvedType = classNamespaces.Select(type => 
     typeLocator.FindTypeExactMatch(activeProject, type + "." + myType)) 
    .FirstOrDefault(type => type != null); 

Bạn cần EnvDTETypeLocator quá.

Đối với VS2015, một ví dụ về hội nhập roslyn thể được tìm thấy tại đây: https://github.com/tomasr/roslyn-colorizer/blob/master/RoslynColorizer/RoslynColorizer.cs

Nó chắc chắn sẽ dễ dàng hơn rất nhiều so với nó là với hiện tại CodeModel.