简体   繁体   中英

Visual Studio: find all references of a specific type

I converted a (C#) struct into a class and need to go through all uses of the type to ensure that there are no undesired effects of the former implicit copy and now reference behaviours.

Is there a way to find all references, where this specific type is used/involved?

I tried Find all References on the type, and get all locations where the type name is explicitely stated, which is a good start.

However, I don't get the locations where an instance of this type is returned and modified. Specifically the lines where I assign the return value to an implicitely typed variable using var , or all assignments to previously defined variables.

Are there any features or tricks I could use? I guess, this particular case of struct-to-class conversion occurs quite often. Maybe you have some advice how to find all possible issues?

You could rename manually the class... every error is one place where it is used. But I think that the Visual Studio will stop after a certain number of errors.

You could mark as [Obsolete] the class, all its properties, all its methods. Then you would have a warning every time you used them.

Note that the [Obsolete] "trick" has some limitations:

[Obsolete]
public class MyClass
{
}

public static void Test1(MyClass test2) // This line has a warning
{
    var y = test2; // ***This line doesn't have a warning***
    MyClass z = test2; // This line has a warning
}

so it is the same as Find all References ...

Another solution, based on FxCop/Code Analysis of Visual Studio:

This is a custom rule, based on the instructions of http://blogs.msdn.com/b/codeanalysis/archive/2010/03/26/how-to-write-custom-static-code-analysis-rules-and-integrate-them-into-visual-studio-2010.aspx

The Default Namespace of the assembly (you can set in the properties) must be MyCustomFxCopRules . The Platform target x86.

using Microsoft.FxCop.Sdk;

namespace MyCustomFxCopRules
{
    public class StructAssignmentFinder : BaseIntrospectionRule
    {
        public StructAssignmentFinder()
            : base("StructAssignmentFinder", "MyCustomFxCopRules.RuleMetadata", typeof(StructAssignmentFinder).Assembly)
        {
            var ms = new MyStruct();
            var tt = ms;
        }

        public override TargetVisibilities TargetVisibility
        {
            get
            {
                return TargetVisibilities.All;
            }
        }

        public override ProblemCollection Check(ModuleNode module)
        {
            Visit(module);
            return Problems;
        }

public override void VisitAssignmentStatement(AssignmentStatement assignment)
{
    // You could even use FullName
    if ((assignment.Source != null && assignment.Source.Type != null && assignment.Source.Type.Name.Name == "MyStruct") ||
        (assignment.Target != null && assignment.Target.Type != null && assignment.Target.Type.Name.Name == "MyStruct"))
    {
        Problem problem = new Problem(GetNamedResolution("Struct", assignment.Target.Type.Name.Name), assignment);
        Problems.Add(problem);
    }

    base.VisitAssignmentStatement(assignment);
}

public override void VisitConstruct(Construct construct)
{
    Method targetMethod = (Method)((MemberBinding)construct.Constructor).BoundMember;

    if (targetMethod.Parameters.Any(x => x.Type.Name.Name == "MyStruct"))
    {
        Problem problem = new Problem(GetNamedResolution("ParameterType", "MyStruct", targetMethod.Name.Name), construct);
        Problems.Add(problem);
    }

    base.VisitConstruct(construct);
}

public override void VisitMethodCall(MethodCall call)
{
    Method targetMethod = (Method)((MemberBinding)call.Callee).BoundMember;

    if (targetMethod.ReturnType.Name.Name == "MyStruct")
    {
        Problem problem = new Problem(GetNamedResolution("ReturnType", targetMethod.ReturnType.Name.Name, targetMethod.Name.Name), call);
        Problems.Add(problem);
    }

    if (targetMethod.Parameters.Any(x => x.Type.Name.Name == "MyStruct"))
    {
        Problem problem = new Problem(GetNamedResolution("ParameterType", "MyStruct", targetMethod.Name.Name), call);
        Problems.Add(problem);
    }

    base.VisitMethodCall(call);
}

The RuleMetadata.xml (it must be an Embedded Resource)

<?xml version="1.0" encoding="utf-8" ?>
<Rules FriendlyName="Rules about Structs">
  <Rule TypeName="StructAssignmentRule" Category="MyRules" CheckId="CR1000">
    <Name>Struct Assignment Finder</Name>
    <Description>Struct Assignment Finder</Description>
    <Url></Url>
    <Resolution Name="Struct">There is an assignment of struct '{0}'.</Resolution>
    <Resolution Name="ReturnType">'{0}' is the return type for a call to '{1}'.</Resolution>
    <Resolution Name="ParameterType">'{0}' is a parameter type for a call to '{1}'.</Resolution>
    <Email></Email>
    <MessageLevel Certainty="100">Warning</MessageLevel>
    <FixCategories>NonBreaking</FixCategories>
    <Owner></Owner>
  </Rule>
</Rules>

Based on this, it is then easy to add other rules for other corner cases that I surely forgot :-)

The test file I was using:

public struct MyStruct
{

}

class Test
{
    public Test()
    {
        var ms = new MyStruct();
        var ms2 = ms;

        ms3 = ms;
        ms = ms3;

        ms4 = ms;
        ms = ms4;

        ms4 = ms4;

        new MyObject(default(MyStruct));
    }

    public MyStruct ms3;
    public MyStruct ms4 { get; set; }
}

public class MyObject
{
    public MyObject(MyStruct par1)
    {

    }
}

Note that debugging rules is tricky... It is quite easy to debug with the FxCopCmd.exe , but impossible (I think, not sure) to debug when called directly by Visual Studio.

The answers and comments led me to a satisfactory solution, that possibly helps others, so I thought I'd post it.

The question was not so much to find all uses, but only the "problematic" ones, namely the write accesses to the public members. Instead of finding all references to the members, as suggested in this answer I only want to find the write access. Therefore, I used a rewriting strategy like indicated in this answer and made all members write only, so the compiler would complain about the dangerous write accesses only.

This in turn transformed the struct/class into an immutable type as outlined in the comments. This made a much cleaner design. I simply rewrote all write accesses where the compiler complained, to respect the immutable design. Thereafter, struct and class are largely exchangable and the switching was easy.

Only trouble were calls to the default constructors of the struct, which was no longer present in the class. But I no longer needed them, since with the class I could simply set these cases to null .

You could do a Find All References on each of the class non-private members, one at a time. It would show every read and write to those members.

If you have Visual Studio Web Essentials Extension installed then its very easy. Check screenshot below. On clicking on highlighted link in red you can see all reference of particular type. 在此输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM