Generic method multiple (OR) type constraint

前端 未结 4 591
无人及你
无人及你 2020-11-27 04:53

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 wi

4条回答
  •  粉色の甜心
    2020-11-27 05:45

    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).

    using System;
    using System.Diagnostics;
    
    namespace Union {
        [DebuggerDisplay("{currType}: {ToString()}")]
        public struct Either {
            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 val) => new Either(val);
    
            public static implicit operator Either(TA val) => new Either(val);
    
            public static implicit operator TP(Either @this) => @this.Primary;
    
            public static implicit operator TA(Either @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 either TP or TA. You can use it as such (the types refer back to my original answer):

    // ...
    public static Either DemoFunc(Either 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 IsPrimary first.
    • You can check any of IsNeither IsPrimary or IsAlternate.
    • You can access the value through Primary and Alternate
    • There are implicit converters between TP/TA and Either to allow you to pass either the values or an Either anywhere where one is expected. If you do pass an Either where a TA or TP is expected, but the Either contains the wrong type of value you'll get a runtime error.

    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.

提交回复
热议问题