SingleTypeParameter

SingleTypeParameter

Summary

Use T as the type parameter name if the type parameter is single.

Default severity

Warning

Description

Names of Classes, Structs, and Interfaces [1] are cited as follows:

Names of Generic Type Parameters

  • ✓ Consider using T as the type parameter name for types with one single-letter type parameter.

However, renaming the type parameter name to T may cause a compilation error/warning or change the meaning, so some cases must be excluded.

🛠️ Changed

Before version 2.1.0, this analyzer issued diagnostics even if the code fix would cause compilation errors or warnings.

How to decide if renaming is possible

Suppose you want to know if the type parameter NotT can be renamed to T without causing a compilation error or warning and changing the meaning.

First, we will discuss the case where a syntax node with the type parameter NotT is a type C (e.g., class, struct, etc.). We cannot rename NotT to T in the following cases:

  • The type name of C is T.
  • One of the members of type C is named T.

For example, renaming the type parameter NotT to T causes a compilation error as follows:

// Renaming NotT to T causes an error CS0694 at the line /*💀*/
/*💀*/ public sealed class T<NotT>
{
}
// Renaming NotT to T causes an error CS0102 at the line /*💀*/
public sealed class U<NotT>
{
    // A MemberDeclaration with the name "T" exists in this scope.
    /*💀*/ public void T()
    {
    }
}

In the example above, the member named T is a method, but it can also be a field, property, delegate, event, or inner type.

We also cannot rename it to avoid compilation warnings in the following cases:

  • The type C has the syntax nodes containing the type parameter T inside C.
  • One of the ancestors of type C is the syntax node containing the type parameter T.

For example:

// Renaming NotT to T causes a warning CS0693 at the line /*💀*/
public sealed class C<NotT>
{
    public sealed class Inner
    {
        /*💀*/ public static class U<T>
        {
        }
    }

    /*💀*/ public void M<T>()
    {
    }
}
public sealed class U<T>
{
    public sealed class Inner
    {
        // Renaming NotT to T causes a warning CS0693 at the line /*💀*/
        /*💀*/ public static class C<NotT>
        {
        }
    }
}

In some cases, we change the meaning of T when we rename NotT to T. If there is a token named T inside type C, NotT cannot be renamed to T unless T is the type name and the declaration of T is inside C. For example:

public sealed class U
{
    public sealed class Inner
    {
        // Renaming NotT to T causes a change the meaning of T in C<NotT>:
        // U.T -> T in C<T>
        public static class C<NotT>
        {
            public static T? Default { get; }
        }
    }

    public sealed class T
    {
    }
}
// Renaming NotT to T does not change the meaning of T in C<NotT>:
public sealed class C<NotT>
{
    public sealed class Inner
    {
        // In this scope, "T" becomes "C<?>.Inner.T", so it is all right.
        public static T Default = new();

        public class T
        {
        }
    }
}

Next, we will discuss the case where a syntax node with the type parameter NotT is a member M (e.g., a method, etc.). We cannot rename NotT to T in the following cases:

  • The member M has a syntax token named T inside M.
  • One of the ancestors of the member M is the syntax node containing the type parameter T.

For example:

public sealed class C
{
    // Renaming NotT to T causes an error CS0412 at the line /*💀*/
    public static int M<NotT>()
    {
        /*💀*/ var T = 0;
        return T;
    }
}
public sealed class C
{
    // Renaming NotT to T causes an error CS0412 at the line /*💀*/
    public static void M<NotT>()
    {
        /*💀*/ static void T()
        {
        }

        T();
    }
}
public sealed class C<T>
{
    public sealed class Inner
    {
        // Renaming NotT to T causes a warning CS0693 at the line /*💀*/
        /*💀*/ public static void M<NotT>()
        {
        }
    }
}

Code fix

The code fix provides an option to replace the type parameter name with T.

Example

Diagnostic

public sealed class Code<Type>
{
    public Code(Type instance)
    {
        Instance = instance;
    }

    public Type Instance { get; }
}

Code fix

public sealed class Code<T>
{
    public Code(T instance)
    {
        Instance = instance;
    }

    public T Instance { get; }
}

References

[1] Microsoft, .NET Framework Design Guidelines