简体   繁体   English

对类型的params数组的约束

[英]Constraint on params array of Types

In the following method: 在以下方法中:

public void Foo(params Type[] types) {}

How can I constrain the types so that each Type passed in must derive from a certain base class? 如何约束types以便传入的每个Type必须派生自某个基类?

I want something along the lines of this: 我想要一些与此类似的东西:

public void Foo(params Type[] types) where each types : BaseClass {}

Clarification: 澄清:

I want the types of the classes that derive from BaseClass , not the instances of them. 我想要从BaseClass派生的类的类型 ,而不是它们的实例。

For example if Bar is a subclass of BaseClass , I want to be able to pass in typeof(Bar) , not new Bar() . 例如,如果BarBaseClass的子类,我希望能够传入typeof(Bar)而不是new Bar()

If you want to pass an array of Type objects, than you can't do what you want. 如果要传递Type对象数组,那么您将无法做自己想做的事情。 That's because constraints are part of the type system - you can set a method to get an argument of type int , or a generic method to receive a generic type parameter derived from IDisposable , or whatever, but what you have there is already a specific, concrete type - Type . 这是因为约束是类型系统的一部分-您可以设置一个方法来获取int类型的参数,或者设置一个通用方法来接收从IDisposable派生的通用类型参数,或者其他任何东西,但是您已经拥有了一个特定的,具体Type

Type , confusingly, isn't part of the type system . Type ,容易混淆,是不是类型系统的一部分。 It's just a regular class that happens to describe the type system metadata . 这只是一个描述类系统元数据的常规类。 But it's just any old class. 但这只是任何旧的类。

Being able to constrain a method to only accept a Type instance whose BaseClass property is MyBaseClass is just like being able to constrain a method to only accept string arguments whose Length == 3 - it's not a question of type (as C# sees it), but of data inside those types. 能够约束一个方法仅接受BaseClass属性为MyBaseClassType实例,就像能够约束一个方法仅接受Length == 3 string参数-这不是类型的问题(如C#所见),但是这些类型里面的数据

Of course, it's not unheard of in other languages to be able to create constrained types - to create a type TLA which is a type of string which must be of length 3 - but that's not how C#'s type system (currently) works. 当然,在其他语言中创建约束类型并不是闻所未闻-创建类型TLA ,它是必须长度为3的string类型-但这不是C#的类型系统(当前)的工作方式。

What you can do is use generic type parameters as a wrapper: 可以做的是使用通用类型参数作为包装器:

public void Foo<T1, T2, T3>() where T1 : BaseClass, T2:BaseClass, T3:BaseClass
{
   // call untyped Foo.
   Foo(typeof(T1), typeof(T2), typeof(T3));
}

This gives you stronger type safety for some scenarios, but it won't give you the ability to pass an unbounded list of types that a params [] will. 在某些情况下,这可以为您提供更强的类型安全性,但无法使您传递params []可以实现的无限类型列表。

This is conceptually similar to wrapping a method which accepts an object parameter with a typed wrapper method. 从概念上讲,这类似于使用类型化包装器方法包装接受object参数的方法。 If your Add(object obj) is confusing, hide it and expose Add(int i) { Add((object)i);} and Add(string s) { Add((object)s);} 如果您的Add(object obj)令人困惑,请将其隐藏并公开Add(int i) { Add((object)i);}Add(string s) { Add((object)s);}

The usual 'kind of workaround' here is to check types that are passed to the method and throw an exception if certain type is not what we expect it to be : 通常的“解决方法”是检查传递给方法的类型,如果某些类型不是我们期望的类型,则抛出异常:

public void Foo(params Type[] types)
{  
     EnsureInheritedFrom<BaseClass>(types);

     // ...rest of stuff
}

private void EnsureInheritedFrom<TBaseType>(IEnumerable<Type> types)
{
     var targetType = typeof(TBaseType);
     foreach(Type type in types)
     {
         if(!targetType.IsAssignbableFrom(type))
         {
             throw new ArgumentException(nameof(types), "Type {type.Name} is not supported!");
         }
     }   
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM