泛型方法多(OR)类型约束

通过阅读 这个,我了解到可以通过使一个方法成为泛型方法来允许该方法接受多个类型的参数。在此示例中,下面的代码与类型约束一起使用,以确保“ U”是 IEnumerable<T>

public T DoSomething<U, T>(U arg) where U : IEnumerable<T>
{
return arg.First();
}

我发现了一些允许添加多个类型约束的代码,比如:

public void test<T>(string a, T arg) where T: ParentClass, ChildClass
{
//do something
}

但是,这段代码似乎强制要求 arg必须同时是 ParentClass 还有 ChildClass的类型。我想说的是,arg 可以是 ParentClass 或者 ChildClass的一种类型,方式如下:

public void test<T>(string a, T arg) where T: string OR Exception
{
//do something
}

感谢您的帮助,一如既往!

126647 次浏览

这是不可能的。但是,您可以为特定类型定义重载:

public void test(string a, string arg);
public void test(string a, Exception arg);

如果它们是泛型类的一部分,那么它们将优先于该方法的泛型版本。

如果 ChildClass 意味着它是从 ParentClass 派生出来的,您可以写下面的内容来同时接受 ParentClass 和 ChildClass;

public void test<T>(string a, T arg) where T: ParentClass
{
//do something
}

另一方面,如果希望使用两种不同的类型,而且它们之间没有继承关系,则应考虑实现相同接口的类型;

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;
}
}

然后你可以写你的泛型函数作为;

public void Test<T>(string a, T arg) where T : ICommonInterface
{
//do something
}

Botz 的回答是100% 正确的,这里有一个简短的解释:

当您编写一个方法(泛型或非泛型)并声明该方法所采用的参数的类型时,您就定义了一个契约:

如果你给我一个对象,它知道如何做 类型 T 知道如何做,我可以传递“ a”: 类型 I 声明,或者“ b”: 使用该类型的某种行为。

如果您尝试一次给它多个类型(通过使用一个或) ,或者尝试让它返回一个可能多于一个类型的值,那么契约就会变得模糊:

如果你给我一个知道如何跳绳或者如何计算 π 的物体 到第15个数字,我会返回一个物体,可以去钓鱼,也许混合 混凝土。

问题是,当你进入这个方法时,你不知道他们是给了你一个 IJumpRope还是一个 PiFactory。此外,当您继续使用这个方法(假设您已经神奇地编译了它)时,您并不确定是否有 FisherAbstractConcreteMixer。基本上这会让整件事变得更加混乱。

解决你问题的方法有两种:

  1. 定义多个方法来定义每个可能的转换、行为或任何东西。这就是 Botz 的回答。在编程世界中,这被称为重载方法。

  2. 定义一个基类或接口,它知道如何执行方法所需的所有操作,并让一个方法采用该类型的 只是。这可能涉及到将 stringException包装在一个小类中,以定义您计划如何将它们映射到实现,但是之后一切都非常清晰和易于阅读。四年后,我可以过来,读你的代码,很容易就能理解发生了什么。

选择哪个取决于选项1和选项2的复杂程度以及它的可扩展性。

所以对于你的具体情况,我会想象你只是从异常中抽出一条消息或者其他东西:

public interface IHasMessage
{
string GetMessage();
}


public void test(string a, IHasMessage arg)
{
//Use message
}

现在您所需要的是将 stringException转换为 IHasMessage 的方法。

尽管这个问题已经存在很久了,但是我的上述解释仍然得到了随机的支持。这个解释仍然非常好,但是我将用一个对我很有用的类型来代替联合类型(这个问题的强类型回答并没有直接得到 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.Neither;
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");
}
}
}
}

上面的类表示的类型可以是 都不是 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());

重要提示:

  • 如果不首先检查 IsPrimary,就会得到运行时错误。
  • 您可以检查任何 IsNeither IsPrimaryIsAlternate
  • 您可以通过 PrimaryAlternate访问该值
  • TP/TA 和 Or < TP,TA > 之间有隐式转换器,允许您在需要传递值的任何地方传递值或 Either。如果您传递一个 Either,其中应该包含 TATP,但是 Either包含了错误类型的值,那么您将得到一个运行时错误。

我通常在希望方法返回结果或错误时使用此方法。它真的清除了样式代码。我也非常偶尔(很少)使用它作为方法重载的替代。实际上,这是一个非常糟糕的替代这种超载。