Skip to main content

How to Resolve Error "CS0306: The type 'type' may not be used as a type argument" in C#

The Compiler Error CS0306 is a type restriction error related to Generics. The message reads: "The type 'Type' may not be used as a type argument".

In C#, Generics (like List<T> or Func<T>) allow you to create reusable code structures. However, the Common Language Runtime (CLR) places restrictions on what T can be. The type argument must be a valid, managed type that can be stored on the heap or used as a standard value type. You cannot use Pointers (int*), Void (void), or certain special runtime types (like System.TypedReference) as generic arguments because they do not conform to the memory management rules of .NET generics.

This guide explains which types are forbidden in generics and how to work around these limitations.

Understanding Invalid Type Arguments

When you define List<T>, the runtime needs to be able to create an array of T.

  • Pointers (int*): Are unmanaged memory addresses. The Garbage Collector cannot track pointers inside a generic container.
  • void: Is not a value. You cannot have a variable of type void, so you cannot have a List<void> (a list of nothings).

If you try to pass these into the angle brackets <...>, the compiler raises CS0306.

Scenario 1: Using Pointers in Generics

This is common when working with Unsafe Code. You might want a list of pointers to integers, but List<int*> is illegal.

Example of error

using System.Collections.Generic;

public unsafe class MemoryManager
{
// ⛔️ Error CS0306: The type 'int*' may not be used as a type argument.
// Generics cannot hold unmanaged pointers.
public List<int*> Pointers = new List<int*>();
}

Solution: Use IntPtr or UIntPtr

The .NET Framework provides System.IntPtr, a safe, managed wrapper around a memory address (pointer). IntPtr is a struct, so it is a valid generic argument.

using System;
using System.Collections.Generic;

public unsafe class MemoryManager
{
// ✅ Correct: IntPtr is a valid struct that can hold a pointer address.
public List<IntPtr> Pointers = new List<IntPtr>();

public void AddPointer(int* ptr)
{
// Convert the pointer to IntPtr to store it
Pointers.Add((IntPtr)ptr);
}

public void UsePointer(int index)
{
// Convert it back to a pointer to use it
int* ptr = (int*)Pointers[index];
Console.WriteLine(*ptr);
}
}
note

Using IntPtr works for storage, but you still need an unsafe context to cast it back to int* and dereference it.

Scenario 2: Using void in Generics

Developers familiar with C++ templates often try to use void to represent "nothing" in a generic return type, or to define a Task that returns nothing.

Example of error

using System;
using System.Threading.Tasks;

public class AsyncWorker
{
// ⛔️ Error CS0306: 'void' is not a valid type argument.
// You cannot return a variable of type 'void'.
public async Task<void> DoWork()
{
await Task.Delay(100);
}

// ⛔️ Error CS0306: Delegate returning void cannot be expressed this way.
public Func<void> MyAction;
}

Solution: Use Non-Generic Alternatives

C# provides specific types for "void" scenarios.

  • For Tasks: Use Task instead of Task<void>.
  • For Delegates: Use Action instead of Func<void>.
using System;
using System.Threading.Tasks;

public class AsyncWorker
{
// ✅ Correct: Use the non-generic Task class for void-return async methods.
public async Task DoWork()
{
await Task.Delay(100);
}

// ✅ Correct: 'Action' is the delegate for methods taking no args and returning void.
public Action MyAction;
}
tip

Functional Programming "Unit": If you absolutely need a generic type that means "nothing" (e.g., to satisfy an interface IResult<T>), use a dummy struct pattern (often called Unit or ValueTuple). IResult<System.ValueTuple> result; (where ValueTuple acts as void).

Scenario 3: Special Runtime Types

Certain types in C# are restricted to the stack and cannot be put on the heap. Because Generics can result in heap allocations (boxing), these types are banned as type arguments.

Examples include:

  • System.ArgIterator
  • System.RuntimeArgumentHandle
  • System.TypedReference
  • ref struct types (like Span<T>) - Note: Span<T> usually raises a specific error CS0611 or CS0306 depending on the compiler version.

Example of error

using System;
using System.Collections.Generic;

public class SpecialHandler
{
// ⛔️ Error CS0306: TypedReference is a special stack-only type.
public List<TypedReference> References;
}

Solution

There is usually no direct workaround for these types in Generics. You must re-architect your code to avoid storing these special types in collections. Process them immediately on the stack.

Conclusion

CS0306 enforces the rules of the Common Language Runtime.

  1. No Pointers: Use IntPtr instead of int* inside <...>.
  2. No Void: Use Action or Task (non-generic) instead of Func<void> or Task<void>.
  3. No Stack-Only Types: Avoid using low-level runtime types like TypedReference in generic lists.