Difference between Class level variable and method

2019-09-14 05:15发布

问题:

I am working on a Web Api for a SPA app using .net. I get CA2000 warnings when i use instantiate an object within a method. But when I declare the same object at the class level, the CA2000 warnings disappear. From below, example 1 gives the CA2000 warning while example 2 does not. Why?

Example 1-

public class CodeGenAPIController : ApiResponseController
{
    NextGenCodeGen.CodeGenerator getEndPoint(TokenManager.TokenData tokenData, int BranchId)
    {
        NextGenCodeGen.CodeGenerator ret = null;
        lock (branchGenLock)
        {
            if (branchGenerators.ContainsKey(BranchId))
                ret = branchGenerators[BranchId];
        }
        if (ret == null)
        {
            string services = ConfigurationValuesAPIController.GetBranchProperties(tokenData.DatabaseIdentifier, BranchId).FirstOrDefault(x => x.Key == "AvailableCodeGenServices").Value;

            string[] endpoints = services.Split(new char[] { ' ', '.' }, StringSplitOptions.RemoveEmptyEntries);
            if (endpoints.Length == 0)
                throw new ArgumentOutOfRangeException("AvailableCodeGenServices",
                    string.Format("There appear to be no Code Generation Services configured for branch {0}", BranchId));

            string endpoint = endpoints[0];
            if (!endpoint.ToLower().EndsWith(".asmx"))
                endpoint = endpoint + ".asmx";

            //OBJECT INSTANTIATION INSIDE THE METHOD
            ret = new My_API.NextGenCodeGen.CodeGenerator() { Url = endpoint, UseDefaultCredentials = true};**
            lock(branchGenLock)
            {
                branchGenerators[BranchId] = ret;
            }
        }
        return ret;         
    }
 }

EXAMPLE 2-

public class CodeGenAPIController : ApiResponseController
{
   //OBJECT INSTANTIATION OUTSIDE THE METHOD AT THE CLASS LEVEL
   NextGenCodeGen.ARGenTCodeGenerator retVal = new My_API.NextGenCodeGen.ARGenTCodeGenerator();

   NextGenCodeGen.CodeGenerator getEndPoint(TokenManager.TokenData tokenData, int BranchId)
    {
        retVal = null;
        lock (branchGenLock)
        {
            if (branchGenerators.ContainsKey(BranchId))
                retVal = branchGenerators[BranchId];
        }
        if (retVal == null)
        {
            string services = ConfigurationValuesAPIController.GetBranchProperties(tokenData.DatabaseIdentifier, BranchId).FirstOrDefault(x => x.Key == "AvailableCodeGenServices").Value;

            string[] endpoints = services.Split(new char[] { ' ', '.' }, StringSplitOptions.RemoveEmptyEntries);
            if (endpoints.Length == 0)
                throw new ArgumentOutOfRangeException("AvailableCodeGenServices",
                    string.Format("There appear to be no Code Generation Services configured for branch {0}", BranchId));

            string endpoint = endpoints[0];
            if (!endpoint.ToLower().EndsWith(".asmx"))
                endpoint = endpoint + ".asmx";
            retVal = new My_API.NextGenCodeGen.CodeGenerator();
            retVal.Url = endpoint;
            retVal.UseDefaultCredentials = true;

            lock (branchGenLock)
            {
                branchGenerators[BranchId] = retVal;
            }
        }
        return retVal;
    }
 }

回答1:

In the class case, it should trip CA2213 and not CA2000. https://msdn.microsoft.com/en-us/library/ms182328.aspx

It looks like there's a limitation of CA2213 that it can't figure out what to do with base classes that implement IDisposable. In the example below, FileIo inherits from IoBase and doesn't raise the warning. FileIo2 only implements IDisposable and does raise the warning.

public class IoBase : IDisposable
{
  public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    protected virtual void Dispose(bool disposing)
    {
    }
}
public class FileIo: IoBase
{
    private Stream io = new FileStream("c:\tmp\tmp.txt", FileMode.OpenOrCreate);
}

public class FileIo2 : IDisposable
{
    private Stream io = new FileStream("c:\tmp\tmp.txt", FileMode.OpenOrCreate);
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    protected virtual void Dispose(bool disposing)
    {
    }
}


回答2:

By declaring the variable at the class level, you are scoping it to the class instance. Even though you re-initialize it in the function, it's scope is still external to the function. Per CA2000 documentation, this does not trigger the warning because "all references to it are out of scope" is not true.

Official doc: https://msdn.microsoft.com/en-us/library/ms182289.aspx

Since the warning was issued in the first example, that should indicate that My_API.NextGenCodeGen.CodeGenerator implements IDisposable. If true, then consider adding a Dispose handler to this class and perform any class-owned IDisposable variable cleanup there. Garbage collection will handle the rest.

If your ApiResponseController inherits ApiController, then you've already got the vehicle, you just gotta drive it:

public abstract class ApiController : IHttpController, IDisposable