简体   繁体   English

无效泛型类型参数的最佳例外

[英]Best exception for an invalid generic type argument

I'm currently writing some code for UnconstrainedMelody which has generic methods to do with enums. 我目前正在为UnconstrainedMelody编写一些代码,它具有与枚举相关的通用方法。

Now, I have a static class with a bunch of methods which are only meant to be used with "flags" enums. 现在,我有一个带有一堆方法的静态类,这些方法只能用于“flags”枚举。 I can't add this as a constraint... so it's possible that they'll be called with other enum types too. 我无法将其添加为约束...所以它们可能也会被其他枚举类型调用。 In that case I'd like to throw an exception, but I'm not sure which one to throw. 在那种情况下,我想抛出异常,但我不确定要抛出哪一个。

Just to make this concrete, if I have something like this: 只是为了使这个具体,如果我有这样的事情:

// Returns a value with all bits set by any values
public static T GetBitMask<T>() where T : struct, IEnumConstraint
{
    if (!IsFlags<T>()) // This method doesn't throw
    {
        throw new ???
    }
    // Normal work here
}

What's the best exception to throw? 投掷的最佳例外是什么? ArgumentException sounds logical, but it's a type argument rather than a normal argument, which could easily confuse things. ArgumentException听起来是合乎逻辑的,但它是一个类型参数而不是普通参数,这很容易混淆事物。 Should I introduce my own TypeArgumentException class? 我应该引入自己的TypeArgumentException类吗? Use InvalidOperationException ? 使用InvalidOperationException NotSupportedException ? NotSupportedException Anything else? 还要别的吗?

I'd rather not create my own exception for this unless it's clearly the right thing to do. 宁愿不为此创建我自己的例外,除非它显然是正确的。

NotSupportedException sounds like it plainly fits, but the documentation clearly states that it should be used for a different purpose. NotSupportedException 听起来很明显,但文档明确指出它应该用于不同的目的。 From the MSDN class remarks: 从MSDN类备注:

There are methods that are not supported in the base class, with the expectation that these methods will be implemented in the derived classes instead. 基类中不支持某些方法,期望这些方法将在派生类中实现。 The derived class might implement only a subset of the methods from the base class, and throw NotSupportedException for the unsupported methods. 派生类可能只实现基类中的方法的子集,并为不支持的方法抛出NotSupportedException。

Of course, there's a way in which NotSupportedException is obviously good enough, especially given its common-sense meaning. 当然,有一种方法可以使NotSupportedException显然足够好,特别是考虑到它的常识意义。 Having said that, I'm not sure if it's just right. 话虽如此,我不确定它是否恰到好处。

Given the purpose of Unconstrained Melody ... 鉴于无约束旋律的目的......

There are various useful things that can be done with generic methods/classes where there's a type constraint of "T : enum" or "T : delegate" - but unfortunately, those are prohibited in C#. 使用泛型方法/类可以完成各种有用的事情,其中​​存在类型约束“T:enum”或“T:delegate” - 但不幸的是,这些在C#中是禁止的。

This utility library works around the prohibitions using ildasm/ilasm ... 这个实用程序库使用ildasm / ilasm解决禁止问题...

... it seems like a new Exception might be in order despite the high burden of proof we justly have to meet before creating custom Exceptions . ...尽管我们在创建自定义Exceptions之前必须满足高度的举证责任,但似乎新的Exception可能是有序的。 Something like InvalidTypeParameterException might be useful throughout the library (or maybe not - this is surely an edge case, right?). InvalidTypeParameterException东西在整个库中可能都很有用(或者可能不是 - 这肯定是一个边缘情况,对吧?)。

Will clients need to be able to distinguish this from BCL Exceptions? 客户是否需要能够将其与BCL例外区分开来? When might a client accidentally call this using a vanilla enum ? 什么时候客户可能会使用香草enum意外地调用它? How would you answer the questions posed by the accepted answer to What factors should be taken into consideration when writing a custom exception class? 您如何回答接受的答案所提出的问题?在编写自定义异常类时应考虑哪些因素?

I would avoid NotSupportedException. 我会避免使用NotSupportedException。 This exception is used in the framework where a method is not implemented and there is a property indicating that this type of operation is not supported. 此异常用于未实现方法的框架中,并且存在指示不支持此类操作的属性。 It doesn't fit here 它不适合这里

I think InvalidOperationException is the most appropriate exception you could throw here. 我认为InvalidOperationException是你可以抛出的最合适的异常。

Generic programming should not throw at runtime for invalid type parameters. 对于无效的类型参数,通用编程不应在运行时抛出。 It should not compile, you should have a compile time enforcement. 它不应该编译,你应该有一个编译时执行。 I don't know what IsFlag<T>() contains, but perhaps you can turn this into a compile time enforcement, like trying to create a type that is only possible to create with 'flags'. 我不知道IsFlag<T>()包含什么,但也许您可以将其转换为编译时执行,就像尝试创建只能使用'flags'创建的类型一样。 Perhaps a traits class can help. 也许traits课可以提供帮助。

Update 更新

If you must throw, I'd vote for InvalidOperationException. 如果你必须抛出,我会投票给InvalidOperationException。 The reasoning is that generic types have parameters and errors related to (method) parameters are centered around the ArgumentException hierarchy. 原因是泛型类型具有参数,并且与(方法)参数相关的错误以ArgumentException层次结构为中心。 However, the recommendation on ArgumentException states that 但是,对ArgumentException的建议说明了这一点

if the failure does not involve the arguments themselves, then InvalidOperationException should be used. 如果失败不涉及参数本身,则应使用InvalidOperationException。

There is at least one leap of faith in there, that method parameters recommendations are also to be applied to generic parameters, but there isn't anything better in the SystemException hierachy imho. 在那里至少有一个信念的飞跃, 方法参数建议也应用于通用参数,但SystemException hierachy imho中没有更好的东西。

I would use NotSupportedException as that is what you are saying. 我会使用NotSupportedException,因为这就是你所说的。 Other enums than the specific ones are not supported . 不支持除特定枚举之外的其他枚举。 This would of course be stated more clearly in the exception message. 当然,这将在异常消息中更清楚地说明。

I'd go with NotSupportedException . 我会选择NotSupportedException While ArgumentException looks fine, it's really expected when an argument passed to a method is unacceptable. 虽然ArgumentException看起来很好,但是当传递给方法的参数是不可接受的时,它确实是预期的。 A type argument is a defining characteristic for the actual method you want to call, not a real "argument." 类型参数是您要调用的实际方法的定义特征,而不是真正的“参数”。 InvalidOperationException should be thrown when the operation you're performing can be valid in some cases but for the particular situation, it's unacceptable. 当您执行的操作在某些情况下有效时,应该抛出InvalidOperationException但对于特定情况,这是不可接受的。

NotSupportedException is thrown when an operation is inherently unsupported. 当操作本身不受支持时,抛出NotSupportedException For instance, when implementing an interface where a particular member doesn't make sense for a class. 例如,在实现某个特定成员对某个类没有意义的接口时。 This looks like a similar situation. 这看起来像是类似的情况。

Apparently, Microsoft uses ArgumentException for that, as demonstrated on example of Expression.Lambda<> , Enum.TryParse<> or Marshal.GetDelegateForFunctionPointer<> in Exceptions section. 显然,Microsoft使用ArgumentException ,如异常部分中的Expression.Lambda <>Enum.TryParse <>Marshal.GetDelegateForFunctionPointer <>的示例所示。 I couldn't find any example indicating otherwise, either (despite searching local reference source for TDelegate and TEnum ). 我找不到任何其他示例(尽管搜索TDelegateTEnum本地参考源)。

So, I think it's safe to assume that at least in Microsoft code it's a common practice to use ArgumentException for invalid generic type arguments aside from basic variable ones. 因此,我认为可以安全地假设至少在Microsoft代码中,除了基本变量之外,使用ArgumentException作为无效泛型类型参数是一种常见做法。 Given that the exception description in docs doesn't discriminate between those, it's not too much of a stretch, either. 鉴于文档中的异常描述不区分这些,它也不是太多。

Hopefully it decides the question things once and for all. 希望它一劳永逸地决定问题。

我同意NotSupportedExpcetion。

Throwing a custom made exception should always be done in any case where it is questionable. 在任何可疑的情况下,都应该始终抛出自定义异常。 A custom exception will always work, regardless of the API users needs. 无论API用户需要什么,自定义异常都将始终有效。 The developer could catch either exception type if he does not care, but if the developer needs special handling he will be SOL. 如果他不关心,开发人员可以捕获任何异常类型,但如果开发人员需要特殊处理,他将是SOL。

I'm always wary of writing custom exceptions, purely on the grounds that they aren't always documented clearly and cause confusion if not named correctly. 我总是对编写自定义异常持谨慎态度,纯粹基于它们并不总是清楚地记录下来并且如果没有正确命名会引起混淆。

In this case I would throw an ArgumentException for the flags check failure. 在这种情况下,我会为标志检查失败抛出ArgumentException。 It's all down to preference really. 这完全取决于偏好。 Some coding standards I've seen go as far as to define which types of exceptions should be thrown in scenarios like this. 我见过的一些编码标准甚至可以定义在这样的场景中应该抛出哪些类型的异常。

If the user was trying to pass in something which wasn't an enum then I would throw an InvalidOperationException. 如果用户试图传入一些不是枚举的东西,那么我会抛出一个InvalidOperationException。

Edit: 编辑:

The others raise an interesting point that this is not supported. 其他人提出了一个有趣的观点,即不支持这一点。 My only concern with a NotSupportedException is that generally those are the exceptions that get thrown when "dark matter" has been introduced to the system, or to put it another way, "This method must go into the system on this interface, but we won't turn it on until version 2.4" 我对NotSupportedException的唯一顾虑是,通常那些是当“暗物质”被引入系统时被抛出的异常,或换句话说,“这个方法必须进入系统在这个界面,但我们赢了直到版本2.4“打开它”

I've also seen NotSupportedExceptions be thrown as a licensing exception "you're running the free version of this software, this function is not supported". 我还看到NotSupportedExceptions被抛出作为许可例外“您正在运行此软件的免费版本,不支持此功能”。

Edit 2: 编辑2:

Another possible one: 另一个可能的:

System.ComponentModel.InvalidEnumArgumentException  

The exception thrown when using invalid arguments that are enumerators. 使用作为枚举数的无效参数时抛出的异常。

How about inheriting from NotSupportedException. 如何继承NotSupportedException。 While I agree with @Mehrdad that it makes the most sense, I hear your point that it doesn't seem to fit perfectly. 虽然我同意@Mehrdad认为它最有意义,但我听到你的观点,它似乎并不完美。 So inherit from NotSupportedException, and that way people coding against your API can still catch a NotSupportedException. 所以从NotSupportedException继承,这样人们对你的API编码仍然可以捕获NotSupportedException。

I'd also vote for InvalidOperationException. 我还投票给InvalidOperationException。 I did an (incomplete) flowchart on .NET exception throwing guidelines based on Framework Design Guidelines 2nd Ed. 我根据Framework Design Guidelines 2nd Ed做了一个关于.NET异常抛出指南的(不完整)流程图 awhile back if anyone's interested. 如果有人有兴趣的话

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

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