通用方法多(OR)类型约束

Man*_*eld 121 c# types asp.net-mvc-3

阅读本文,我了解到有可能允许方法通过使其成为通用方法来接受多种类型的参数.在该示例中,以下代码与类型约束一起使用以确保"U"是一个IEnumerable<T>.

public T DoSomething<U, T>(U arg) where U : IEnumerable<T>
{
    return arg.First();
}
Run Code Online (Sandbox Code Playgroud)

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

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

然而,这个代码出现到强制执行arg必须是一个类型的ParentClass ChildClass.我想要做的是说arg可以是一种类型ParentClass ChildClass以下列方式:

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

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

Bot*_*000 59

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

public void test(string a, string arg);
public void test(string a, Exception arg);
Run Code Online (Sandbox Code Playgroud)

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

  • @Mansfield我不知道确切的原因,但我认为你将无法以有意义的方式使用通用参数.在课堂内,如果允许他们完全不同的类型,他们将被视为对象.这意味着你也可以省略泛型参数并提供重载. (3认同)
  • @Mansfield,这是因为`或`关系使得一般事物变得有用.你要做*反思来弄清楚要做什么以及所有这些.(YUCK!). (3认同)
  • 有趣的。我想到了这一点,但我认为使用一个函数可能会使代码更清晰。嗯,非常感谢!只是出于好奇,您是否知道这是不可能的具体原因?(是否有意将其排除在语言之外?) (2认同)

Cri*_*ole 26

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

当您编写方法(通用或非通用)并声明方法所采用的参数类型时,您将定义合同:

如果你给我一个知道如何做类型T知道如何做的事情的对象,我可以提供'a':我声明的类型的返回值,或'b':某种使用的行为那种类型.

如果你尝试一次给它多个类型(通过拥有一个或)或者试图让它返回一个可能超过一种类型的值,那么契约会变得模糊:

如果你给我一个知道如何跳绳或知道如何计算pi到第15位的物体,我将返回一个可以钓鱼或混凝土的物体.

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

您的问题的解决方案是两种可能性之一:

  1. 定义多个定义每个可能的转换,行为或其他的方法.这是Botz的答案.在编程世界中,这被称为重载方法.

  2. 定义一个基类或接口,它知道如何完成方法所需的所有操作,并让一个方法只采用该类型.这可能涉及结束了一个stringException在一个小类来定义它们映射到落实你怎么打算,但后来一切都超清晰易读.我可以在四年后来,阅读你的代码并轻松了解正在发生的事情.

您选择哪个取决于选择1和2的复杂程度以及它需要的可扩展性.

因此,对于您的具体情况,我会想象您只是从异常中提取消息或其他内容:

public interface IHasMessage
{
    string GetMessage();
}

public void test(string a, IHasMessage arg)
{
    //Use message
}
Run Code Online (Sandbox Code Playgroud)

现在您只需要将a string和a Exception转换为IHasMessage的方法.很容易.


dar*_*yal 7

如果ChildClass意味着它是从ParentClass派生的,那么您可以编写以下内容来接受ParentClass和ChildClass;

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

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以写你的通用函数;

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


Cri*_*ole 6

和这个问题一样古老,我仍然在上面的解释中得到随机的赞成。解释仍然完全正确,但我将第二次回答一个类型,它可以很好地替代联合类型(对 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");
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

上述类代表一种类型,可以是任一TPTA。您可以这样使用它(类型参考我的原始答案):

// ...
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());
Run Code Online (Sandbox Code Playgroud)

重要笔记:

  • 如果不先检查,就会出现运行时错误IsPrimary
  • 您可以检查IsNeither IsPrimary或 中的任何一个IsAlternate
  • 您可以通过Primary和访问该值Alternate
  • TP/TA 和Either<TP, TA> 之间有隐式转换器,允许您传递值或Either任何需要的地方。如果您确实传递了Eitherwhere a TAorTP是预期的,但Either包含错误类型的值,您将收到运行时错误。

我通常在我想要一个方法返回结果或错误的地方使用它。它确实清理了该样式代码。我也非常偶尔(很少)使用它来替代方法重载。实际上,这是这种过载的非常糟糕的替代品。