通过


解决与泛型类型参数和泛型类型参数相关的错误和警告

本文介绍以下编译器错误:

  • CS0080:不允许 对非泛型声明使用约束。
  • CS0081类型参数声明必须是标识符,而不是类型。
  • CS0224具有 vararg 的方法不能为泛型、处于泛型类型或具有参数参数。
  • CS0304无法创建变量类型的实例,因为它没有 new() 约束。
  • CS0305使用泛型类型需要 N 类型参数。
  • CS0306类型不能用作类型参数。
  • CS0307标识符不是泛型方法。如果打算使用表达式列表,请围绕表达式使用括号。
  • CS0308非泛型类型或方法不能与类型参数一起使用。
  • CS0310类型必须是具有公共无参数构造函数的非抽象类型,才能将其用作泛型类型或方法中的参数。
  • CS0311类型不能用作泛型类型或方法中的类型参数 T 。没有从 type1 到 type2 的隐式引用转换。
  • CS0312类型“type1”不能用作泛型类型或方法中的类型参数。可以为 null 的类型“type1”不满足约束。
  • CS0313类型“type1”不能用作泛型类型或方法中的类型参数。可以为 null 的类型“type1”不满足约束。可以为 Null 的类型不能满足任何接口约束。
  • CS0314此类型不能用作泛型类型或泛型方法的类型参数。不存在装箱转换或类型参数转换。
  • CS0315类型不能用作泛型类型或方法中的类型参数 T 。没有装箱转换。
  • CS0401约束 new() 必须是指定的最后一个约束。
  • CS0403无法将 null 转换为类型参数,因为它可能是不可为 null 的值类型。请考虑改用 default(T)
  • CS0405类型参数的重复约束。
  • CS0412:参数:参数、局部变量或本地函数不能与方法类型参数同名。
  • CS0413类型参数不能与运算符一起使用 as ,因为它没有类类型约束,也没有 class 约束。
  • CS0417标识符:创建变量类型的实例时无法提供参数。
  • CS0449class不能组合或复制约束,structunmanagednotnulldefault并且必须先在约束列表中指定。
  • CS0450类型参数:不能同时指定约束类或classstruct约束。
  • CS0451new() 约束不能与 struct 约束一起使用。
  • CS0454涉及类型参数 1 和类型参数 2 的循环约束依赖项。
  • CS0455类型参数继承冲突约束。
  • CS0694类型参数的名称与包含的类型或方法相同。
  • CS0695T 无法实现这两个接口,因为它们可能会统一某些类型参数替换。
  • CS0698泛型类型无法从类型派生,因为它是属性类。
  • CS0702约束不能是特殊类。
  • CS0703可访问性不一致:约束类型的可访问性低于声明。
  • CS0706约束类型无效。用作约束的类型必须是接口、非密封类或类型参数。
  • CS0717静态类:静态类不能用作约束。
  • CS1961方差无效:类型参数在类型上必须是有效的变体。
  • CS7002意外使用泛型名称。
  • CS8322无法将具有动态类型的参数传递给具有推断类型参数的泛型本地函数。
  • CS9011关键字 delegate 不能用作约束。你的意思是 System.Delegate吗?
  • CS9012意外关键字 record。你的意思是 record struct 还是 record class
  • CS9338访问级别不一致:类型的访问性低于类。

类型参数声明和命名

以下错误与在泛型类型和方法中声明和命名类型参数的方式相关:

  • CS0080:不允许 对非泛型声明使用约束。
  • CS0081类型参数声明必须是标识符,而不是类型。
  • CS0412:参数:参数、局部变量或本地函数不能与方法类型参数同名。
  • CS0694类型参数的名称与包含的类型或方法相同。
  • CS9012意外关键字 record。你的意思是 record struct 还是 record class

若要更正这些错误,请确保声明具有有效标识符的类型参数,将约束子句仅应用于泛型声明,并避免与范围中的其他标识符的命名冲突:

  • 从非泛型声明中删除约束子句(CS0080)。 子 where 句只能用于声明类型参数的泛型类型和方法,因为约束定义类型参数必须满足的要求。 如果需要应用约束,请先向类型或方法声明添加类型参数。 例如,将 public class MyClass where MyClass : System.IDisposable 更改为 public class MyClass<T> where T : System.IDisposable
  • 将实际类型名称替换为类型参数声明(CS0081)中的标识符。 必须使用标识符(如 TTKeyTValue)而不是具体类型(如 intstring)声明类型参数。 类型参数的目的在于作为一个占位符,在泛型类型或方法被使用时,编译器会将其替换为实际的类型。 例如,将 public void F<int>() 更改为 public void F<T>()
  • 重命名类型参数、局部变量或参数以避免命名冲突(CS0412CS0694)。 类型参数名称不能在同一范围内隐藏标识符。 无法匹配包含类型或方法的名称。 此类冲突会创建关于要引用的标识符的歧义。 例如,如果有方法 public void F<T>(),则不能在该方法中声明局部变量 double T ,并且不能将类型参数命名为与其包含类型相同的类型(class C<C>)。
  • 使用正确的记录声明语法(CS9012)。 声明记录类型时,必须使用 record classrecord struct (或仅 record 用于引用类型)。 record仅关键字就不能出现在编译器需要类型声明语法的位置。 例如,如果您打算声明记录类型,请写 record class MyRecordrecord struct MyRecord,而不是放 record 在其他关键字预期的地方。

有关详细信息,请参阅 泛型类型参数泛型

约束声明和排序

以下错误与泛型类型参数的约束的语法和顺序相关:

  • CS0401约束 new() 必须是指定的最后一个约束。
  • CS0449class不能组合或复制约束,structunmanagednotnulldefault并且必须先在约束列表中指定。
  • CS0450类型参数:不能同时指定约束类或classstruct约束。
  • CS0451约束new()不能与约束struct一起使用。
  • CS9011关键字 delegate 不能用作约束。你的意思是 System.Delegate吗?

类型参数的约束必须遵循特定顺序:主要约束(class、、structunmanagednotnulldefault)先行,后跟接口或类约束,最后new()是构造函数约束。 某些约束是相互排斥的,不能组合。

若要更正这些错误,请执行以下步骤:

  • new() 约束置于约束列表的末尾(CS0401)。 该 new() 约束必须出现在所有其他约束之后。 例如,将 where T : new(), IDisposable 更改为 where T : IDisposable, new()
  • 先放置主要约束,不合并互斥约束(CS0449)。 最多可以指定一个 classstructunmanagednotnulldefault,并且它必须首先出现在约束列表中。 约束classstruct是相互排斥的,classunmanaged也是如此。 在可以为 null 的上下文中, class 已经表示 notnull,因此无法将它们组合在一起。
  • 不要将特定类约束与 (struct) 组合在一起。 如果类型参数受特定类类型的约束,则它隐式为引用类型,这与约束相矛盾 struct 。 删除类约束或 struct 约束。
  • 不要将 new()structCS0451)合并。 所有值类型(结构)都隐式具有公共无参数构造函数,因此约束 new() 在与 struct 结合使用时是冗余的。 使用struct时,删除new()的约束。
  • System.Delegate替换约束子句中的delegateCS9011)。 关键字 delegate 用于声明委托类型,而不是作为约束。 若要将类型参数约束为委托类型,请 System.Delegate 用作约束类型。 例如,将 where T : delegate 更改为 where T : System.Delegate

以下示例显示了正确的约束排序:

using System;

// Primary constraint first, then interface constraints, then new()
class C<T> where T : class, IDisposable, new() { }

// struct doesn't need new() - it's implicit
class D<T> where T : struct, IComparable { }

// Delegate constraint using System.Delegate
class E<T> where T : System.Delegate { }

有关详细信息,请参阅类型参数的约束

类型参数数量与用法

以下错误与向泛型类型和方法提供正确的类型参数数和类型相关:

  • CS0224具有 vararg 的方法不能为泛型、处于泛型类型或具有参数参数。
  • CS0305使用泛型类型需要 N 类型参数。
  • CS0306类型不能用作类型参数。
  • CS0307标识符不是泛型方法。如果打算使用表达式列表,请围绕表达式使用括号。
  • CS0308非泛型类型或方法不能与类型参数一起使用。
  • CS7002意外使用泛型名称。

若要更正这些错误,请确保提供泛型声明所需的类型参数的确切数量。 仅将有效类型用作类型参数。 不要将类型参数应用于非泛型构造:

  • 从使用 __arglistCS0224) 的方法中删除泛型类型参数或包含泛型类型声明。 关键字 __arglist 与泛型不兼容,因为处理变量参数列表的运行时机制与泛型类型参数所需的类型替换冲突。 当与泛型方法或泛型类型中的方法结合使用时, params 此限制也适用于关键字。
  • 提供泛型类型或方法声明(CS0305)中指定的类型参数的确切数量。 实例化泛型类型时,定义中声明的每个泛型类型参数都必须具有相应的类型参数。 编译器需要知道要替换每个类型参数的具体类型。 例如,如果类声明为 class MyList<T>,则必须在使用类时提供一个类型参数,例如 MyList<int>,而不是 MyList<int, string>
  • 仅将有效类型用作类型参数(CS0306)。 指针类型(如 int*char*)不能用作类型参数,因为泛型类型需要垃圾回收器可以跟踪的托管类型,并且指针类型是非托管的。 如果需要在泛型上下文中使用指针,请考虑使用 IntPtr 或重组代码以避免将泛型与不安全代码混合。
  • 从非泛型构造中删除类型参数语法(CS0307CS0308)。 括在尖括号(like <int>)中的类型参数只能应用于声明类型参数的泛型类型和方法。 必须完全删除类型参数,或确保已导入包含该类型的泛型版本的命名空间。 例如, IEnumerator<T> 需要 using System.Collections.Generic; 指令,而 IEnumerator 位于 System.Collections.
  • 从不支持泛型的声明中删除类型参数(CS7002)。 某些构造(如枚举)不能为泛型。 如果需要枚举值的泛型容器,请考虑改用泛型类或结构。

有关详细信息,请参阅 泛型类型参数泛型

构造函数约束

以下错误与 new() 泛型类型参数的约束相关:

  • CS0304无法创建变量类型的实例,因为它没有 new() 约束。
  • CS0310类型必须是具有公共无参数构造函数的非抽象类型,才能将其用作泛型类型或方法中的参数。
  • CS0417标识符:创建变量类型的实例时无法提供参数。

若要更正这些错误,请将 new() 约束添加到需要实例化的类型参数,确保类型参数具有公共无参数构造函数,并避免在构造类型参数的实例时传递参数:

  • new() 约束添加到类型参数声明(CS0304)。 使用 new 运算符在泛型类型或方法中创建类型参数的实例时,编译器必须能够保证运行时提供的任何类型参数都具有可用的无参数构造函数。 约束 new() 在编译时提供此保证,使编译器能够生成适当的实例化代码。 例如,如果您有 class C<T> 和一个成员 T t = new T();,则必须将声明更改为 class C<T> where T : new()
  • 确保与约束类型参数一起使用 new() 的类型参数具有公共无参数构造函数(CS0310)。 泛型类型或方法在类型参数上声明 new() 约束时,用作类型参数的任何具体类型必须是非抽象的,并且必须提供公共无参数构造函数。 如果类型只有非公共构造函数(例如 privateprotected 构造函数),或者只有具有参数的 new() 构造函数,则它不能满足约束。 若要修复此错误,请将公共无参数构造函数添加到该类型或使用已具有该参数的其他类型参数。
  • 实例化类型参数时删除构造函数参数(CS0417)。 该 new() 约束仅保证无参数构造函数的存在,因此无法将参数 new T(arguments) 传递给,因为编译器无法验证具有这些特定参数类型的构造函数是否存在于被 T替换的类型上。 如果需要使用特定参数构造实例,请考虑使用工厂方法、抽象工厂模式或定义所需构造行为的特定基类或接口约束。

有关详细信息,请参阅 类型参数的约束new() 约束

约束满意度和转换

以下错误与不符合泛型类型参数约束的类型参数相关:

  • CS0311类型不能用作泛型类型或方法中的类型参数 T 。没有隐式引用转换。
  • CS0312类型不能用作泛型类型或方法中的类型参数。可以为 null 的类型不满足约束。
  • CS0313类型不能用作泛型类型或方法中的类型参数。可以为 null 的类型不满足约束。可以为 Null 的类型不能满足任何接口约束。
  • CS0314该类型不能作为泛型类型或方法中的类型参数,因为不存在装箱转换或类型参数转换。
  • CS0315类型无法在泛型类型或方法TypeorMethod<T>中用作类型参数T。没有装箱转换。

若要更正这些错误,请使用通过适当转换满足所有约束的类型参数,确保派生类重复基类约束,并了解可为 null 的值类型具有特殊的约束要求:

  • 将类型参数更改为具有对约束类型(CS0311)的隐式引用转换的类型参数。 当类型参数具有类似 where T : BaseType约束时,任何类型参数都必须通过隐式引用转换或标识转换转换为 BaseType 。 如果类型参数是接口,则必须是 BaseType 自身、派生自 BaseType或实现 BaseType 。 隐式数值转换(如从 shortint)不满足泛型类型参数约束,因为这些转换是值转换,而不是引用转换。
  • 在任何派生类声明(CS0314)中重复基类的类型参数约束。 当派生泛型类继承自对其类型参数具有约束的基泛型类时,派生类必须对其相应的类型参数声明相同的约束。 必须重复这些约束,因为编译器需要验证提供给派生类的类型参数是否满足基类的要求。 例如,如果有 public class A<T> where T : SomeClass,则必须将派生自它的任何类声明为 public class B<T> : A<T> where T : SomeClass
  • 使用不可为 null 的值类型或更改约束类型(CS0312CS0313)。 可为 Null 的值类型(例如 int?)不同于其基础值类型,并且不满足相同的约束。 之间不存在int?int之间的隐式转换,并且可为 null 的值类型不能满足接口约束,因为可为 null 的包装器本身并未实现接口,而基础值类型虽然实现了。 为了解决这些错误,可以使用值类型的非空形式作为类型参数,或者根据需要调整约束以接受object或可为空的引用类型。
  • 确保类型参数满足引用类型或类约束(CS0315)。 当类型参数被限制为类类型(如 where T : SomeClass)时,不能将值类型(结构体)用作类型参数,因为没有满足这种约束关系的装箱转换。 约束需要具有与约束类型的继承或实现关系的引用类型。 若要解决此错误,请在语义上适当地将结构更改为类,或者如果泛型类型可以使用值类型,则删除类约束。

有关详细信息,请参阅 类型参数隐式转换的约束。

泛型类型使用限制

以下错误与如何使用泛型类型的限制有关:

  • CS0403无法将 null 转换为类型参数,因为它可能是不可为 null 的值类型。请考虑改用 default(T)
  • CS0413类型参数不能与运算符一起使用 as ,因为它没有类类型约束,也没有 class 约束。
  • CS0695类型不能实现这两个接口,因为它们可能会统一某些类型参数替换。
  • CS0698泛型类型无法从类型派生,因为它是属性类。
  • CS8322无法将具有动态类型的参数传递给具有推断类型参数的泛型本地函数。
  • CS9338可访问性不一致:类型的可访问性比类低。

若要更正这些错误,请使用 default 而不是 null 用于非约束类型参数,在使用 as 运算符时添加类约束,避免接口统一冲突,避免创建泛型属性类,并确保类型参数与其包含成员的可见性匹配:

  • 将分配替换为nulldefault(T)或添加class约束(CS0403)。 当您将null分配给一个不受约束的类型参数时,编译器无法保证该类型参数是可接受null值的引用类型,因为它可能是类似intstruct的值类型,无法成为null。 若要解决此错误,请使用default(T),它为任何类型(引用类型为 null、值类型为零或为空)提供适当的默认值;如果特别需要引用类型语义并希望允许class赋值,请向类型参数添加null约束。
  • 使用class运算符时添加as或指定的类型约束(CS0413)。 as运算符执行一个安全类型强制转换,返回null如果转换失败。但此行为与值类型不兼容,因为值类型不能null。 在与不受约束的类型参数一起使用as时,编译器无法确保类型参数不是值类型,因此会拒绝该代码。 若要修复此错误,请添加 class 约束或特定的引用类型约束(例如 where T : SomeClass),以确保类型参数始终是能够正确处理 null 失败转换结果的引用类型。
  • 避免多次使用可以统一的类型参数实现同一泛型接口(CS0695)。 当类多次实现具有不同类型参数(例如 class G<T1, T2> : I<T1>, I<T2>)的泛型接口时,有人可能会为这两个参数使用相同的类型实例化该接口(G<int, int>这会导致冲突),因为该类实际上会实现 I<int> 两次。 若要解决此错误,请仅实现一次接口,重新构造类型参数以防止统一,或者对不同的专用化使用单独的非泛型类。
  • 从属性类中删除泛型类型参数(CS0698)。

    注释

    此错误不会在当前版本的 C# 中生成,因为现在支持泛型属性。

  • 将动态值传递给泛型本地函数时显式指定类型参数(CS8322)。 将参数传递给 dynamic 泛型本地函数时,编译器无法推断类型参数,因为在运行时之前,实际类型未知。 若要修复此错误,请显式指定类型参数(例如 LocalFunc<int>(d)),将动态值强制转换为预期类型,或使用非动态变量。
  • 确保公共签名或受保护签名中使用的类型参数至少与使用它们的成员一样可访问(CS9338)。 公共或受保护的泛型成员必须使用可公开访问的类型参数。 否则,外部代码无法正确引用或使用成员的签名。 例如,如果你有 public class Container<T> ,其中 T 是内部类型,则外部程序集可以看到 Container ,但无法正常使用,因为它们不能看到 T。 若要修复此错误,请将类型参数设为公开,或降低使用它的成员的访问级别,以匹配类型参数的访问级别。

有关详细信息,请参阅 类型参数默认值表达式特性的约束。

有效约束类型

以下错误与将无效类型用作泛型类型参数的约束有关:

  • CS0405类型参数的重复约束。
  • CS0702约束不能是特殊类。
  • CS0703可访问性不一致:约束类型的可访问性低于声明。
  • CS0706约束类型无效。用作约束的类型必须是接口、非密封类或类型参数。
  • CS0717static class静态类不能用作约束。

约束必须是接口、非密封类或类型参数。 由于某些类型在 .NET 类型系统中具有特殊含义,或者因为它们不能被继承,这些类型作为约束是无效的。

若要更正这些错误,请执行以下步骤:

  • 删除重复约束(CS0405)。 每个约束只能在约束子句中出现一次。 如果有 where T : I, I,请删除重复项。
  • 不要将特殊类用作约束(CS0702)。 类型 ObjectArrayValueType 不能用作约束。 每个类型都已经派生自 Object,所以对其进行约束没有任何意义。 ArrayValueType 是无法直接继承的抽象基类型。 如果需要类似数组的行为,请使用IList<T>IEnumerable<T>
  • 确保约束类型至少与泛型类型(CS0703)一样可访问。 公共泛型类型无法使用内部类型的约束,因为外部代码无法提供有效的类型参数。 使约束类型公开,或减少泛型类型的可访问性。
  • 仅使用接口、非密封类或类型参数作为约束(CS0706)。 不能将数组、密封类、结构、枚举或其他无效类型用作约束。 如果需要特定行为,请考虑使用所需类型实现的接口。
  • 不要将静态类用作约束(CS0717)。 静态类无法扩展,因为它们仅包含静态成员。 不存在派生自静态类的类型,因此不能将其用作约束。 请改用非静态类或接口。

以下示例显示了有效的约束类型:

public interface IMyInterface { }
public class MyBaseClass { }

// Valid: interface constraint
class A<T> where T : IMyInterface { }

// Valid: non-sealed class constraint
class B<T> where T : MyBaseClass { }

// Valid: type parameter constraint
class C<T, U> where T : U { }

有关详细信息,请参阅类型参数的约束

约束冲突和循环依赖项

以下错误与约束声明中的约束或循环依赖项之间的冲突相关:

  • CS0454涉及类型参数 1 和类型参数 2 的循环约束依赖项。
  • CS0455类型参数继承冲突约束。

约束无法创建循环依赖项,类型参数无法同时继承无法同时满足的冲突约束。

若要更正这些错误,请执行以下步骤:

  • 删除循环约束依赖项(CS0454)。 类型参数不能直接或间接地通过其约束依赖于自身。 例如, where T : U where U : T 创建循环依赖项,因为 T 依赖于 UU 依赖 T。 通过删除其中一个约束来中断周期。
  • 删除冲突继承的约束(CS0455)。 类型参数不能限制为多个不相关的类,因为 C# 不支持多个类继承。 同样,它不能同时约束 struct 和类类型,因为这些约束是互斥的。 重构类型层次结构或删除其中一个冲突约束。

以下示例显示了问题:

// CS0454: Circular dependency - T depends on U and U depends on T
class Circular<T, U> where T : U where U : T { }

// CS0455: Conflicting constraints - U can't derive from both B and B2
public class B { }
public class B2 { }
public class G<T> where T : B
{
    public class N<U> where U : B2, T { }
}

有关详细信息,请参阅类型参数的约束

类型参数差异

以下错误与泛型类型参数的方差修饰符相关:

  • CS1961方差无效:类型参数在类型上必须是有效的变体。

方差修饰符(in 表示逆变,out 表示协变)控制如何在接口和委托声明中使用类型参数。 协变(out) 类型参数只能出现在输出位置(返回类型),而逆变类型in参数只能出现在输入位置(参数类型)。

若要更正此错误,请:

  • 对仅在返回类型中显示的类型参数使用 out (协变)。 协变允许在预期派生类型较少的情况下使用更多的派生类型。
  • 对仅出现在参数类型中的类型参数使用 in (逆变)。 逆变允许在预期更多派生类型的情况下使用更少的派生类型。
  • 如果类型参数必须同时出现在输入和输出位置中,请删除方差修饰符。

以下示例显示了正确和不正确的方差用法:

// Incorrect: out T can't appear in input position
interface IWrong<out T>
{
    void Method(T arg);  // CS1961
}

// Correct: out T only in output positions
interface ICovariant<out T>
{
    T GetValue();
}

// Correct: in T only in input positions
interface IContravariant<in T>
{
    void Process(T arg);
}

// No modifier needed for both input and output
interface IInvariant<T>
{
    T Transform(T arg);
}

有关详细信息,请参阅 泛型中的协变和逆变