简体   繁体   中英

field 'FIELD' is never assigned to, and will always have its default value

private struct TOKEN_USER
    {
        internal SID_AND_ATTRIBUTES User; //Compiler warning comes from here.
    }

[StructLayout(LayoutKind.Sequential)]
private struct SID_AND_ATTRIBUTES
   {
       internal IntPtr Sid;
       private int Attributes;
   }

Initializing the structure to default:

TOKEN_USER tokenUser = default(TOKEN_USER);

Then making the two required calls to retrieve the pointer to the structure: (not relevant to the question) using this:

GetTokenInformation(tokenhandle, TokenInformationClass.TokenUser, sid, sidlength, ref sidlength);

and then marshalling back to a structure.

tokenUser = (TOKEN_USER)Marshal.PtrToStructure(sid, tokenUser.GetType());

which works, but the compiler warns me that the 'User' field in TOKEN_USER is unassigned.

R# suggests me initializing it from the constructor:

public TOKEN_USER(SID_AND_ATTRIBUTES user) : this(user)
        {
        }

However, this doesn't compile, with error "Constructor cannot call itself". My question is, should i just assign it to SID_AND_ATTRIBUTES (default) to meet the compiler's requirement, or ignore it?

Test program:

   [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr OpenProcess(
        int dwDesiredAccess,
        [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
        int dwProcessId);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool OpenProcessToken(
        IntPtr processHandle,
        int desiredAccess,
        ref IntPtr TokenHandle);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool GetTokenInformation(
        IntPtr tokenHandle,
        TokenInformationClass tokenInformationClass,
        IntPtr tokenInformation,
        int TokenInformationLength,
        ref int ReturnLength);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool IsValidSid(
        IntPtr SID);


    private enum TokenInformationClass
    {
        TokenUser = 1,
    }

    private const int QueryInformation = 0x400;

    private const int TokenRead = 0x20008;

    private struct TOKEN_USER
    {
        internal SID_AND_ATTRIBUTES User; //Compiler warning comes from here.
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct SID_AND_ATTRIBUTES
    {
        internal IntPtr Sid;
        private int Attributes;
    }



    internal static IntPtr GetProcessHandle()
    {
        foreach (Process p in Process.GetProcesses())
        {
            using (p)
            {
                if (p.ProcessName == "explorer")
                {
                    return OpenProcess(QueryInformation, false, p.Id);
                }
            }
        }
        return IntPtr.Zero;
    }

    public void Test()
    {
        IntPtr pHandle = GetProcessHandle();

        IntPtr tokenHandle = IntPtr.Zero;
        OpenProcessToken(pHandle, TokenRead, ref tokenHandle);

        int sidlength = 0;
        GetTokenInformation(tokenHandle, TokenInformationClass.TokenUser, IntPtr.Zero, 
0, ref sidlength);

        TOKEN_USER tokenUser = default(TOKEN_USER);
        IntPtr sid = Marshal.AllocHGlobal(sidlength);

        GetTokenInformation(tokenHandle, TokenInformationClass.TokenUser,sid, 
sidlength, ref sidlength);

        tokenUser = (TOKEN_USER)Marshal.PtrToStructure(sid, tokenUser.GetType());


        if (IsValidSid(tokenUser.User.Sid))
        {
            Debug.WriteLine("Valid!");
        }
    }

You will see this warning any time that you use types which are initialized via some external mechanism, such as reflection or (as here) the Marshal class. The issue is that the compiler has no way to know how that field is initialized; all it can see is the type itself, and there is nothing in the type that would initialize that field.

Since declaring a field which is never assigned a value is often a coding mistake, the compiler generates a warning for the case.

I'm not sure what the exact suggestion R# was, but obviously you can't use the same constructor overload as the one you're declaring, because that would create an infinitely recursive initialization.

In some cases, it makes sense to initialize the struct using : this() (ie calling the default constructor), but that's for dealing with issues where you want to access a field or property in the constructor and you can't until it's initialized (calling the this() constructor initializes the entire object). Maybe that's what R# was "thinking" of?


Anyway, my approach in this situation is simply to disable the warning, as I know for sure it's a false positive. I do initialize the field, just not in a way that the compiler knows about. You can accomplish this with the #pragma warning directive. Eg:

private struct TOKEN_USER
{
    #pragma warning disable 0649
    internal SID_AND_ATTRIBUTES User;
    #pragma warning restore 0649
}

See related discussion at the answer to Suppressing “is never used” and “is never assigned to” warnings . There, it is suggested that you include a comment explaining why the warning is being disabled and why that's safe; IMHO, that's a very good idea in any situation where you disable a warning.

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