Casting an object to two interfaces at the same time, to call a generic method

前端 未结 7 1760
我寻月下人不归
我寻月下人不归 2020-12-09 18:27

I want to call a generic method that constrains the input type T to implement two interfaces:

interface IA { }
interface IB { }
void foo(T t) where          


        
7条回答
  •  误落风尘
    2020-12-09 19:13

    I'm not condoning this practice, but here are two options that others didn't mention:

    If you have control over foo

    Then you can refactor it to:

    void foo(IA asA, IB asB)
    {
        if (!ReferenceEquals(isA, isB)) throw new ArgumentException("isA and isB must be the same object");
        // Your code here
    }
    

    This allows your calling code to become:

    foo(obj as IA, obj as IB);
    

    It isn't pretty, but it might be an option.

    If you like pandemic hacks

    If you find yourself needing to do this a lot, then that's a bad smell and there's a better design.

    But if you have no choice and this is "needed" all over the place, then this might make your life easier because you don't have to waste time creating classes that implement your IA and IB interfaces:

    /// 
    /// An ugly hack for when you don't want to create a new wrapper class that inherits from and implements two other interfaces
    /// 
    /// 
    /// 
    public sealed class MultiType
    {
        /// 
        /// The contained item
        /// 
        private readonly object _containedObject;
    
        /// 
        /// The contained item as a TOne
        /// 
        public TOne AsOne => (TOne)_containedObject;
    
        /// 
        /// The contained item as a TTwo
        /// 
        public TTwo AsTwo => (TTwo)_containedObject;
    
        /// 
        /// Creates a new MultiType that exposes the given item as two different classes
        /// 
        /// 
        private MultiType(object containedObject)
        {
            if (containedObject is TOne && containedObject is TTwo)
                _containedObject = containedObject;
            else
                throw new Exception("The given object must be both a TOne and a TTwo");
        }
    
        /// 
        /// Creates a new MultiType that exposes the given thing as both a TOne and a TTwo
        /// 
        /// 
        /// 
        /// 
        public static MultiType Create(T thing)
            where T : TOne, TTwo
            => new MultiType(thing);
    }
    

    Usage:

    void foo(MultiType thing)
    {
        thing.AsOne... // Your code dealing with the thing as an IA
        thing.AsTwo... // Your code dealing with the thing as an IB
    }
    

    Caller:

    foo(MultiType.Create(obj))
    

    Note that this can be "chained": an instance of MultiType, IList>, MultiType would let you deal with a thing as a dictionary, list of integers, plain enumerable, INotifyPropertyChanged, and INotifyCollectionChanged, all at once.

    But again, it's a really bad code smell--likely the class that needs to be dealt with in that way is doing way too much.

提交回复
热议问题