C# 泛型方法多重 (OR) 类型约束
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10833918/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Generic method multiple (OR) type constraint
提问by Mansfield
Reading this, I learned it was possible to allow a method to accept parameters of multiple types by making it a generic method. In the example, the following code is used with a type constraint to ensure "U" is an IEnumerable<T>.
阅读这篇文章,我了解到可以通过使其成为通用方法来允许方法接受多种类型的参数。在示例中,以下代码与类型约束一起使用以确保“U”是IEnumerable<T>.
public T DoSomething<U, T>(U arg) where U : IEnumerable<T>
{
return arg.First();
}
I found some more code which allowed adding multiple type constraints, such as:
我发现了更多允许添加多种类型约束的代码,例如:
public void test<T>(string a, T arg) where T: ParentClass, ChildClass
{
//do something
}
However, this code appears to enforce that argmust be both a type of ParentClassandChildClass. What I want to do is say that arg could be a type of ParentClassorChildClassin the following manner:
但是,此代码似乎强制arg必须同时是ParentClass和的类型ChildClass。我想要做的是说 arg 可以是以下方式的一种ParentClass或ChildClass方式:
public void test<T>(string a, T arg) where T: string OR Exception
{
//do something
}
Your help is appreciated as always!
一如既往地感谢您的帮助!
采纳答案by Botz3000
That is not possible. You can, however, define overloads for specific types:
这是不可能的。但是,您可以为特定类型定义重载:
public void test(string a, string arg);
public void test(string a, Exception arg);
If those are part of a generic class, they will be preferred over the generic version of the method.
如果这些是泛型类的一部分,那么它们将优先于方法的泛型版本。
回答by daryal
If ChildClass means it is derived from ParentClass, you may just write the following to accept both ParentClass and ChildClass;
如果 ChildClass 意味着它是从 ParentClass 派生的,您可以编写以下内容来接受 ParentClass 和 ChildClass;
public void test<T>(string a, T arg) where T: ParentClass
{
//do something
}
On the otherhand, if you want to use two different types with no inheritance relation between them, you should consider the types implementing the same interface;
另一方面,如果你想使用两个不同的类型,它们之间没有继承关系,你应该考虑实现相同接口的类型;
public interface ICommonInterface
{
string SomeCommonProperty { get; set; }
}
public class AA : ICommonInterface
{
public string SomeCommonProperty
{
get;set;
}
}
public class BB : ICommonInterface
{
public string SomeCommonProperty
{
get;
set;
}
}
then you can write your generic function as;
那么你可以将你的泛型函数写为;
public void Test<T>(string a, T arg) where T : ICommonInterface
{
//do something
}
回答by Chris Pfohl
Botz answer is 100% correct, here's a short explanation:
Botz 的回答 100% 正确,这里有一个简短的解释:
When you are writing a method (generic or not) and declaring the types of the parameters that the method takes you are defining a contract:
当您编写一个方法(通用或非通用)并声明该方法采用的参数类型时,您正在定义一个协定:
If you give me an object that knows how to do the set of things that Type T knows how to do I can deliver either 'a': a return value of the type I declare, or 'b': some sort of behavior that uses that type.
如果你给我一个对象,它知道如何做类型 T 知道如何做的一组事情,我可以提供“a”:我声明的类型的返回值,或“b”:使用的某种行为那种类型。
If you try and give it more than one type at a time (by having an or) or try to get it to return a value that might be more than one type that contract gets fuzzy:
如果您尝试一次给它多个类型(通过使用 or)或尝试让它返回一个可能是多个类型的值,合同变得模糊:
If you give me an object that knows how to jump rope or knows how to calculate pi to the 15th digit I'll return either an object that can go fishing or maybe mix concrete.
如果你给我一个知道如何跳绳或知道如何计算 pi 到 15 位数字的对象,我将返回一个可以钓鱼的对象,或者可能混合混凝土。
The problem is that when you get into the method you have no idea if they've given you an IJumpRopeor a PiFactory. Furthermore, when you go ahead and use the method (assuming that you've gotten it to magically compile) you're not really sure if you have a Fisheror an AbstractConcreteMixer. Basically it makes the whole thing way more confusing.
问题是,当您进入该方法时,您不知道他们是否给了您一个IJumpRope或一个PiFactory. 此外,当您继续使用该方法时(假设您已经神奇地编译了它),您不确定是否有 aFisher或AbstractConcreteMixer. 基本上它使整个事情变得更加混乱。
The solution to your problem is one of two possiblities:
您的问题的解决方案是以下两种可能性之一:
Define more than one method that defines each possible transformation, behavior, or whatever. That's Botz's answer. In the programming world this is referred to as Overloading the method.
Define a base class or interface that knows how to do all the things that you need for the method and have one method take justthat type. This may involve wrapping up a
stringandExceptionin a small class to define how you plan on mapping them to the implementation, but then everything is super clear and easy to read. I could come, four years from now and read your code and easily understand what's going on.
定义多个方法来定义每个可能的转换、行为或其他任何内容。这是博茨的回答。在编程世界中,这称为重载方法。
定义一个基类或接口,该基类或接口知道如何执行该方法所需的所有操作,并使一个方法仅采用该类型。这可能涉及将 a
stringand包装Exception在一个小类中以定义您计划如何将它们映射到实现,但是一切都非常清晰且易于阅读。四年后,我可以来阅读您的代码并轻松了解发生了什么。
Which you choose depends on how complicated choice 1 and 2 would be and how extensible it needs to be.
您选择哪个取决于选择 1 和 2 的复杂程度以及它需要的可扩展性。
So for your specific situation I'm going to imagine you're just pulling out a message or something from the exception:
因此,对于您的特定情况,我将想象您只是从异常中提取消息或其他内容:
public interface IHasMessage
{
string GetMessage();
}
public void test(string a, IHasMessage arg)
{
//Use message
}
Now all you need are methods that transform a stringand an Exceptionto an IHasMessage. Very easy.
现在您需要的是将 astring和 anException转换为 IHasMessage 的方法。好简单。
回答by Chris Pfohl
As old as this question is I still get random upvotes on my explanation above. The explanation still stands perfectly fine as it is, but I'm going to answer a second time with a type that's served me well as a substitute for union types (the strongly-typed answer to the question that's not directly supported by C# as is).
和这个问题一样古老,我仍然在上面的解释中得到随机的赞成。解释仍然完全正确,但我将第二次回答一个类型,它可以很好地替代联合类型(对 C# 不直接支持的问题的强类型答案,原样) )。
using System;
using System.Diagnostics;
namespace Union {
[DebuggerDisplay("{currType}: {ToString()}")]
public struct Either<TP, TA> {
enum CurrType {
Neither = 0,
Primary,
Alternate,
}
private readonly CurrType currType;
private readonly TP primary;
private readonly TA alternate;
public bool IsNeither => currType == CurrType.Primary;
public bool IsPrimary => currType == CurrType.Primary;
public bool IsAlternate => currType == CurrType.Alternate;
public static implicit operator Either<TP, TA>(TP val) => new Either<TP, TA>(val);
public static implicit operator Either<TP, TA>(TA val) => new Either<TP, TA>(val);
public static implicit operator TP(Either<TP, TA> @this) => @this.Primary;
public static implicit operator TA(Either<TP, TA> @this) => @this.Alternate;
public override string ToString() {
string description = IsNeither ? "" :
$": {(IsPrimary ? typeof(TP).Name : typeof(TA).Name)}";
return $"{currType.ToString("")}{description}";
}
public Either(TP val) {
currType = CurrType.Primary;
primary = val;
alternate = default(TA);
}
public Either(TA val) {
currType = CurrType.Alternate;
alternate = val;
primary = default(TP);
}
public TP Primary {
get {
Validate(CurrType.Primary);
return primary;
}
}
public TA Alternate {
get {
Validate(CurrType.Alternate);
return alternate;
}
}
private void Validate(CurrType desiredType) {
if (desiredType != currType) {
throw new InvalidOperationException($"Attempting to get {desiredType} when {currType} is set");
}
}
}
}
The above class represents a type that can be eitherTP orTA. You can use it as such (the types refer back to my original answer):
上述类代表一种类型,可以是任一TP或TA。您可以这样使用它(类型参考我的原始答案):
// ...
public static Either<FishingBot, ConcreteMixer> DemoFunc(Either<JumpRope, PiCalculator> arg) {
if (arg.IsPrimary) {
return new FishingBot(arg.Primary);
}
return new ConcreteMixer(arg.Secondary);
}
// elsewhere:
var fishBotOrConcreteMixer = DemoFunc(new JumpRope());
var fishBotOrConcreteMixer = DemoFunc(new PiCalculator());
Important Notes:
重要笔记:
- You'll get runtime errors if you don't check
IsPrimaryfirst. - You can check any of
IsNeitherIsPrimaryorIsAlternate. - You can access the value through
PrimaryandAlternate - There are implicit converters between TP/TA and Either to allow you to pass either the values or an
Eitheranywhere where one is expected. If you dopass anEitherwhere aTAorTPis expected, but theEithercontains the wrong type of value you'll get a runtime error.
- 如果不先检查,就会出现运行时错误
IsPrimary。 - 您可以检查
IsNeitherIsPrimary或 中的任何一个IsAlternate。 - 您可以通过
Primary和访问该值Alternate - TP/TA 和Either 之间有隐式转换器,允许您传递值或
Either任何需要的地方。如果您确实传递了Eitherwhere aTAorTP是预期的,但Either包含错误类型的值,您将收到运行时错误。
I typically use this where I want a method to return either a result or an error. It really cleans up that style code. I also very occasionally (rarely) use this as a replacement for method overloads. Realistically this is a very poor substitute for such an overload.
我通常在我想要一个方法返回结果或错误的地方使用它。它确实清理了该样式代码。我也非常偶尔(很少)使用它来替代方法重载。实际上,这是这种过载的非常糟糕的替代品。

