将类型限制为特定类型

发布于 2024-09-13 12:35:41 字数 340 浏览 4 评论 0原文

是否可以将泛型方法限制在特定类型上?

我想写这样的东西:

public T GetValue<T>(string _attributeValue) where T : float, string
{
    return default(T); // do some other stuff in reality
}

我主要是试图避免在方法内有一个巨大的 switch 语句,或者如果指定了无效类型则必须抛出异常。

编辑:确认。我知道 string 不是值类型。我早些时候开始使用两种数字类型。对不起。

Is it possible to constrain a generic method on specific types?

I want to write something like this:

public T GetValue<T>(string _attributeValue) where T : float, string
{
    return default(T); // do some other stuff in reality
}

I'm mostly just trying to avoid having a giant switch statement inside the method or having to throw an exception if an invalid type is specified.

Edit: Ack. I knew string is not a value type. I started out with two numeric types earlier. Sorry.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

萌化 2024-09-20 12:35:41

您不能使用泛型约束来表达您感兴趣的限制。泛型并不意味着表达基于不相交类型的变化 - 它们意味着表达在类型层次结构上统一的变化(或那些实现某些接口的)。

不过,您还有一些替代选择。您选择哪个取决于您想要做的事情的确切性质。

使用不同命名的方法来表达每个操作。当每个方法真正执行不同的操作时,我倾向于使用这种方法。您可能会争辩说,从方法返回不同类型的值本质上是不同的操作,并且应该有自己唯一的名称。

float GetFloat(string attrName) { }
string GetString(string attrName) { }

提供“默认值”以允许推断类型。在许多通过名称请求值的设计中,提供默认值很有用。这可以让您使用重载来区分要调用的方法(基于默认值的类型)。不幸的是,这种方法非常脆弱——并且在将文字值传递给接受数字基元(int、uint、long)的重载时很容易崩溃。

float GetValue(string attrName, float defaultValue) { ... }
string GetValue(string attrName, string defaultValue) { ... }

使用泛型方法,但如果该类型不是您支持的类型之一,则抛出运行时异常。我个人认为这种丑陋并且违反了泛型的精神 - 泛型应该统一功能实现某个接口的层次结构或一组类型。但是,在某些情况下,这样做是有意义的(假设无法支持一种特定类型)。这种方法的另一个问题是,无法从任何参数推断泛型方法的签名,因此您必须在调用它时指定所需的类型......此时它并没有好多少(从语法的角度来看)而不是具有不同的方法名称。

T GetValue<T>( string attrName )
{
   if( typeof(T) != typeof(string) ||
       typeof(T) != typeof(float) )
       throw new NotSupportedException();
   return default(T); 
}

// call it by specifying the type expected...
float f = GetValue<float>(attrName);
string s = GetValue<string>(attrName);

使用输出参数而不是返回值。这种方法效果很好,但它失去了能够调用方法并作用于返回值的简洁语法,因为您首先必须声明一个变量来填充。

void GetValue( string attrName, out float value )
void GetValue( string attrName, out string value )

// example of usage:
float f;
GetValue( attrName, out f );
string s;
GetValue( attrName, out s );

You can't use generic constraints to express the limitations you are interested in. Generics are not meant to express variation based on disjoint types - they're meant to express variation that is unified over a hierarchy of types (or those implementing certain interfaces).

You have a few alternative choices, however. Which you choose depends on the exact nature of what you're trying to do.

Use differently named methods to express each operation. I tend to use this approach when each method is truly doing something different. You could argue that returning a different type of value from a method is essentially a different operation, and deserves its own unique name.

float GetFloat(string attrName) { }
string GetString(string attrName) { }

Provide a "default value" to allow the type to be inferred. In many designs where you ask for a value by name it useful to supply a default value. This can allow you to employ overloading to differentiate between which method to invoke (based on the type of the default value). Unfortunately, this approach is quite fragile - and breaks easily when passing literal values to overloads that accept numeric primitives (int vs. uint vs. long).

float GetValue(string attrName, float defaultValue) { ... }
string GetValue(string attrName, string defaultValue) { ... }

Use a generic method, but throw a runtime exception if the type isn't one of those you support. Personally I find this kind of ugly and in violation of the spirit of generics - generics should unify functionality over a hierarchy or a set of types implementing some interface. However, in some cases it makes sense to do so (if let's so one specific type cannot be supported, let's say). Another problem with this approach is that the signature of the generic method cannot be inferred from any parameters, so you would have to specify the type desired when calling it ... at which point it's not much better (from a syntax point of view) than having different method names.

T GetValue<T>( string attrName )
{
   if( typeof(T) != typeof(string) ||
       typeof(T) != typeof(float) )
       throw new NotSupportedException();
   return default(T); 
}

// call it by specifying the type expected...
float f = GetValue<float>(attrName);
string s = GetValue<string>(attrName);

Use an out parameter instead of a return value. This approach works well, but it loses the concise syntax of being able to call a method and act on a return value, since you first have to declare a variable to populate.

void GetValue( string attrName, out float value )
void GetValue( string attrName, out string value )

// example of usage:
float f;
GetValue( attrName, out f );
string s;
GetValue( attrName, out s );
月下凄凉 2024-09-20 12:35:41

这是不可能通过编译时支持来实现的。您可以在静态构造函数中执行此检查并抛出异常(在类型上定义 T 的情况下)或在方法主体本身中(在您的情况下),但在这种情况下,这将是运行时验证。

This is not possible to do with compile time support. You can do this check in the static constructor and throw an exception (in the case the T is defined on the type) or (in your case) in the method body itself, but in that case it would be a runtime validation.

忆依然 2024-09-20 12:35:41

不,您不能指定类型范围。如果您想要所有原语,您可以这样做(我知道不包括字符串)

 where T: struct

No, you can not specify a range of types. If you want all primatives you can do (and i know string is not included)

 where T: struct
沧笙踏歌 2024-09-20 12:35:41

不,这是不可能的。

并且字符串是引用类型,而不是值类型。

您可以获得的最接近的是对所有值类型(减去可为 Null 类型)的约束:

public T GetValue<T>(string _attributeValue) where T : struct

根据您在方法内实际执行的操作,可能有多种方法可以实现您的目标(除了 switch/case 之外)。考虑更改您的示例以使其更有意义...

另一种选择也可能是将您的方法设为私有并提供特定的公共包装器:

private T GetValue<T>(string _attributeValue) where T : struct
{
    return default(T);
}

public float GetFloatValue(string _attributeValue)
{
    return GetValue<float>(_attributeValue);
}

public int GetIntValue(string _attributeValue)
{
    return GetValue<int>(_attributeValue);
}

这将允许您将类的公共成员限制为所需的类型,但仍然在内部使用通用代码,这样您就不必重复自己。

No, it's not possible.

And string is a reference type, not a value type.

The closest you can get is constraining on all value types (minus Nullable types):

public T GetValue<T>(string _attributeValue) where T : struct

Depending on what you're actually doing inside the method, there may be various ways to achieve your goal (other than switch/case). Consider changing your example to be a little more meaningful...

One other option might also be to make your method private and provide public wrappers that are specific:

private T GetValue<T>(string _attributeValue) where T : struct
{
    return default(T);
}

public float GetFloatValue(string _attributeValue)
{
    return GetValue<float>(_attributeValue);
}

public int GetIntValue(string _attributeValue)
{
    return GetValue<int>(_attributeValue);
}

That would allow you to constrain the public members of your class to the desired types but still use generic code internally so you don't have to repeat yourself.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文