使用泛型约束限制类型 [英] Restrict type with Generics Constraints

查看:59
本文介绍了使用泛型约束限制类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

大家好,

我想用Generics Constraint建立一个关于以下问题的例子:

当用户输入字符串类型但是合理时,编译器会如何捕获错误int,float,double,long type?

你能帮帮我解决这个问题吗?

非常感谢。

解决方案

< blockquote>这不是一个很好的技巧,但是字符串不是的数字类实现的唯一接口是 IFormattable [ ^ ]

所以你可以做的就是在你的课程中包含一个约束,如下所示:

  public   class  MyNumericClass< T> 
其中 T:IFormattable
{
// 类方法和属性
}



然后你可以像下面那样实例化你的类。请注意,最后两行在编译时会出错,因为 char string 没有实现IFormattable接口。

 MyNumericClass< short> shortVal =  new  MyNumericClass< short>(); 
MyNumericClass< int> intVal = new MyNumericClass< int>();
MyNumericClass< long> longVal = new MyNumericClass< long>();
MyNumericClass< double> doubleVal = new MyNumericClass< double>();
MyNumericClass< float> floatVal = new MyNumericClass< float>();
MyNumericClass< decimal> decimalVal = new MyNumericClass< decimal>();
// 下面的行会出错没有从[type]到'的隐式引用转换编译时System.IFormattable'
//
MyNumericClass< char> charVal = new MyNumericClass< char>();
MyNumericClass< string> stringVal = new MyNumericClass< string>();





如果您需要要在以后进行计算,您可能需要查看 [ ^ ]更高级的文章。


你不能用泛型来做到这一点。



也许你甚至误解了关于泛型约束的东西。它们不限制类型,它们与运行时无关。它们是泛型类型的泛型参数的约束,而不是泛型类型的约束。如果您尝试使用某些完整类型作为实际泛型参数来实例化泛型类型来替换受约束泛型参数并违反约束,那么您将无法编译代码。它发生在编译时期间。在某种程度上,它实际上扩展了泛型类型的功能,因为您可以使用通用参数的成员,否则这些成员将无法访问,因为无约束的参数类型是完全未知的。



要理解所有这些,你只需要阅读它,但不是从通用参数约束开始,而是从泛型的开头: http://msdn.microsoft.com/en-us/library/512aeb7t%28v=vs.80%29.aspx [ ^ ]。



所以,从现在开始,让我们暂时忘记仿制药,它们与你的问题无关。您可能希望确保用户输入的字符串无法解释为某种基本类型(或者类型为code> System.DateTime ),以及它输入字符串不符合类型所需的字符串格式,句柄以某种方式,通常要求用户修复字符串或再次正确输入。



有两个相关的方法。首先,我想在您使用某些输入控件时过滤掉一些情况,并过滤掉某些输入模式不允许的字符。它很有用,但在许多(实际上几乎所有)案例中都无法解决问题。例如,如果你想允许用户输入一个浮点数,你应该允许'。'字符,但没有任何东西会阻止用户输入两个点(并且任何强制执行一个点的尝试都会非常混乱,甚至不尝试)。某些特殊控件可以确保100%有效输入,但对于有限的情况(例如给定范围和固定步长的数字,日期/时间等等)。有些人尝试使用正则表达式,但这完全是愚蠢的:过于复杂,几乎无法保证正确的结果。



所以,让我们考虑最常见的情况。首先,尝试将字符串解释为某种类型的对象称为解析。可以表示为字符串的每种类型定义了许多名为 Parse * TryParse * 的方法。 />


第一种方法是:不要分析字符串,只是尝试使用 Parse * 方法之一解析它。如果失败,它将抛出一个你可以处理的异常。像这样:

  int   value ; 
尝试 {
value = INT .Parse(theUserInputString);
catch (例外){
// 做点什么
}
// 成功;不是你有价值,可以检查其他东西,比如说,有效的价值范围





但是,对于大多数情况,第二种方法更为典型:使用

 e< code> TryParse * < /  代码 > 方法:
double ;
bool success = double .Parse(theUserInputString, out value );
if (!success){
// 做点什么
}
// 成功;不是你得到了价值,并且可以查看其他内容,例如,有效的值范围
// 或者,因为这是浮点值,如果它不是NaN或无穷大则是chech





我希望,你明白了。



那么,你能编写一个通用的方法,你可以用任何一种原始方法来做,所以这个用户可能只会用一些具体的参数类型实例化它?不幸的是不?这不是C ++模板。这里的问题非常深刻。您不能将任何值类型用作泛型参数的约束。阅读约束,你会看到。我看到了一些建议解决方法的文章,但它并没有真正帮助:它实际上等同于为这样的抽象参数呈现整个运算符集,这对于所有预定义类型重复上面显示的所有代码都不再可行。为什么不允许这样的限制?嗯,这也是非常根深蒂固的。特别是,这与值类型缺乏继承有关。 (是的,有些语言可以使用值甚至原始类型作为派生类型的基本类型,但这是一个完全不同的故事,它与.NET无关......)



-SA


如果我理解你的要求,那就是目前如何实现泛型的缺点。



定义泛型类 MyClass< T> 时,您可以指定约束,例如 MyClass< T>其中T:class 将T限制为只有类类型(任何类类型)。

MyClass< T>其中T:YourClass 仅限于YourClass(和派生)。

MyClass< T>其中T:ISomething 仅用于实现指定接口的类。

MyClass< T>其中T:struct 只有结构(值类型)。



不能做什么(还有) )是 MyClass< T>其中T:enum MyClass< T>其中T:数字



如何编译器捕获违规行为并不重要。



http:// msdn。 microsoft.com/en-US/library/ms379564(v=VS.80).aspx [ ^ ]


Hi everyone,
I want to build a example about Generics Constraint with the question:
How will the complier catch a error when user input string type but reasonable with int, float, double, long type?
Can you help me this problem?
Many thanks.

解决方案

This will not be a nice trick, but the only interface implemented by numeric classes that string doesn't is IFormattable[^]
So what you can do is to include a constraint on your class, like below:

public class MyNumericClass<T>
       where T : IFormattable
   {
      //class methods and properties
   }


And then you can instantiate your class like below. Notice that the last two lines will error on compile time since char and string does not implement IFormattable interface.

MyNumericClass<short> shortVal = new MyNumericClass<short>();
MyNumericClass<int> intVal = new MyNumericClass<int>();
MyNumericClass<long> longVal = new MyNumericClass<long>();
MyNumericClass<double> doubleVal = new MyNumericClass<double>();
MyNumericClass<float> floatVal = new MyNumericClass<float>();
MyNumericClass<decimal> decimalVal = new MyNumericClass<decimal>();
// the lines below would error out with "There is no implicit reference conversion from [type] to 'System.IFormattable'"
// on compile time.
MyNumericClass<char> charVal = new MyNumericClass<char>();
MyNumericClass<string> stringVal = new MyNumericClass<string>();



If you need to make calculations later on, you might want to check on this[^] more advanced article.


You cannot really do this with generics.

Maybe you even misunderstood something about generic constraints. They don't restrict type, and they are not related to runtime. They are constraints for the generic parameters of a generic type, not for generic types. You simply won't be able to compile code if you try to instantiate the generic type with some complete type used as an actual generic parameter to substitute the constrained generic parameter with violation of a constraint. It happens during compile time. In a way, it actually extends the functionality of the generic type, as you can use the members of the generic parameters which otherwise would be inaccessible, because the unconstrained parameter type is fully unknown.

To understand all that, you just need to read about it, but starting not from generic parameters constraints, but from the very beginning of generics: http://msdn.microsoft.com/en-us/library/512aeb7t%28v=vs.80%29.aspx[^].

So, from this moment, let's forget generics for now, they are irrelevant to your problem. You probably want to make sure that it the user enters the string which could not be interpreted as certain primitive type (or such type as, notably, System.DateTime), and, it the input string does not conform to the string format required by the type, handle is somehow, typically, ask the user to fix the string or input it again, correctly.

There are two related approached to this. First of all, I want to set aside cases when you use some input control and filter out some characters which are not allowed for certain input pattern. It is useful, but won't solve the problem in many (in fact, nearly all) cases. For example, if you want to allow the user to enter a floating-point number, you should allow '.' character, but nothing will prevent the user to enter, say, two dots (and any attempt to enforce one will be extremely confusing, don't even try). Some special controls can ensure 100% valid input, but for limited cases (such as the number with given range and fixed step, date/time, and a lot more). Some try to use Regular Expression, but this is totally silly: too complex, and correct result is almost never guaranteed.

So, let's consider the most general case. First of all, an attempt to interpret a string as an object of some type is called parsing. Each type which can be represented as a string defines a number of methods named as Parse* and TryParse*.

First approach is this: instead of analyzing the string, just try to parse it with one of the Parse* methods. If it fails, it will throw an exception which you can handle. Like this:

int value;
try {
    value = int.Parse(theUserInputString);
catch (Exception) {
    // do something about it
}
// success; not you got the value and can check up something else, say, for a valid range of values



However, the second approach is more typical for most cases: the use of on

e of <code>TryParse*</code> methods:
double value;
bool success = double.Parse(theUserInputString, out value);
if (!success) {
   // do something about it
}
// success; not you got the value and can check up something else, say, for a valid range of values
// or, as this is floating-point value, chech if it is not NaN or infinity



I hope, you got the idea.

So, could you write one single generic method which you would do it with any, say, primitive type, so the user of this could would only instantiate it with some concrete parameter type? Unfortunately not? This is not C++ template. The problem here is really deep. You cannot use any of the value types as a constraint of the generic parameters. Read about constraints and you will see. I saw some article where a work-around is advised, but it's not really helpful: it is actually equivalent to presenting a whole operator set for such abstracted parameter, which is no more feasible then simply repeating all the codes shown above for all predefined types. And why such constraints are not allowed? Well, this is also very deeply rooted. In particular, this is related to lack of inheritance for value types. (Yes, there are languages where value and even primitive types can be used as base types for derived types, but this is a whole different story which has nothing to do with .NET…)

—SA


If I understand what you are asking, that's a shortcoming of how generics are currently implemented.

When defining a generic class MyClass<T> you can specify constraints, for instance MyClass<T> where T : class to restrict T to only class types (any class type).
Or MyClass<T> where T : YourClass to only YourClass (and derived).
Or MyClass<T> where T : ISomething to only classes that implement a specified Interface.
Or MyClass<T> where T : struct to only structs (value types).

What you can't do (yet) is MyClass<T> where T : enum and MyClass<T> where T : numeric .

How the compiler catches violations doesn't matter.

http://msdn.microsoft.com/en-US/library/ms379564(v=VS.80).aspx[^]


这篇关于使用泛型约束限制类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆