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

时间:2012-05-31 12:46:31

标签: c# asp.net-mvc-3 types

阅读this,我了解到有可能允许方法通过使其成为通用方法来接受多种类型的参数。在示例中,以下代码与类型约束一起使用以确保“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
}

您的帮助一如既往地受到赞赏!

4 个答案:

答案 0 :(得分:52)

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

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

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

答案 1 :(得分:24)

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

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

  

如果你给我一个知道怎么做那些东西的对象   类型T知道怎么做我可以提供'a':a的返回值   类型I声明,或'b':某种使用该类型的行为。

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

  

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

问题在于,当您进入该方法时,您不知道他们是否已经为您提供了IJumpRopePiFactory。此外,当你继续使用这个方法时(假设你已经让它神奇地编译),你不确定你是否有FisherAbstractConcreteMixer。基本上它会让整个事情变得更加混乱。

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

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

  2. 定义一个基类或接口,它知道如何完成该方法所需的所有操作,并使用一个方法 该类型。这可能涉及在一个小类中包含stringException来定义您计划如何将它们映射到实现,但随后一切都非常清晰且易于阅读。我可以在四年后来,阅读你的代码并轻松了解正在发生的事情。

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

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

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

    现在您只需要将stringException转换为IHasMessage的方法。很容易。

答案 2 :(得分:6)

如果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
}

答案 3 :(得分:1)

与这个问题一样古老,我在上面的解释中仍然得到随机投票。解释仍然可以很好地解决,但是我将使用适合于我的类型(作为联合类型的替代品)(对于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");
            }
        }
    }
}

上面的类表示可以是 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和Either之间存在隐式转换器,以使您可以将值或Either传递到任何期望的地方。如果您传递了一个EitherTA所在的TP,但是Either包含错误的值类型,您将得到运行时错误。

我通常在需要方法返回结果或错误的地方使用此方法。它确实清除了该样式代码。我也偶尔(很少)用它代替方法重载。实际上,这是对这种超载的非常差的替代。