How to call a method overload based on closed generic type?

后端 未结 6 2019
时光取名叫无心
时光取名叫无心 2021-02-13 10:20

Suppose I have three methods:

void Foo(MemoryStream v) {Console.WriteLine (\"MemoryStream\");}
void Foo(Stream v)       {Console.WriteLine (\"Stream\");}
void Fo         


        
相关标签:
6条回答
  • 2021-02-13 10:49

    I had the same issue, and the unique solution I knew was try-casting it until I got something other than null. Then, I would have the correct type at compile time and the compiler would know the right overload to call. I could not find another way to achieve this 'run-time polymorphism'.

    To avoid using a dictionary or a switch-like solution (poor maintainability,as pointded out by Marc), just call Method((dynamic) o) and DLR will call the correct overload method according to the run-time type.

    Just remember to:

    1) Provide a default overload with the most top type possible;

    2) Watch out for any ambiguity during resolution of type at run-time (i.e. two indepenent interfaces and one implementation that uses both);

    3) Handle null case.

    You can read more about it here.

    Hope I've helped.

    0 讨论(0)
  • 2021-02-13 10:51

    This is not a "pretty" answer (indeed, since this is kinda subverting the intent of generics, it is hard to find a pretty answer inside the language), but you could perhaps code the overload lookups via a dictionary:

    static readonly Dictionary<Type, Action<object>> overloads
        = new Dictionary<Type, Action<object>> {
            {typeof(Stream), o => Foo((Stream)o)},
            {typeof(MemoryStream), o => Foo((MemoryStream)o)}
        };
    public static void Bar<T>() {
        Action<object> overload;
        if (overloads.TryGetValue(typeof(T), out overload)) {
            overload(default(T));
        } else {
            Foo((object)default(T));
        }
    }
    

    This isn't nice, and I don't recommend it. For easier maintenance, you could perhaps move the overloads population to a static constructor / type initalizer, and populate it via reflection. Note also that this only works for exact T - it won't work if someone uses an unexpected type (Bar<NetworkStream> for example) - although you could presumably loop over the base-types (but even then, it doesn't have great support for interfaces etc).

    This approach doesn't have much to recommend it, all things considered. I would probably advise approaching the entire problem from a different angle (i.e. removing the need to do this).

    0 讨论(0)
  • 2021-02-13 10:53

    This can only be done using dynamic binding, e.g. like this:

    void Bar<T>(T value)
    {
        dynamic parameter = value;
        Foo(parameter); 
    }
    

    Note that dynamic dispatch uses the actual runtime type of the actual runtime object to do method dispatch, so there has to be an object. If value is null, this will not work.

    0 讨论(0)
  • 2021-02-13 11:03

    What you describe is called "template specialization" and doesn't work in C#. It is available in C++ but still hasn't made its way to C#.

    This has already been answered in "C# generic interface specialization". The short version is that you can't do it. You can work around it forcing runtime resolution but in this case using generics makes no sense. Generics should be used to use the same code on different types.

    Perhaps there is another way of doing what you really want. I've run in similar situations when implementing the Strategy or Template Method patterns, where I want most of the code to work in the general case but modify some specific steps.

    In such cases it's better to inject the custom steps to your class as interfaces, or even Func<> objects that specialize the behavior, when you actually create the "Template Method".

    Of course, there are a lot of other ways to do this, some of which work better than others for specific problems

    0 讨论(0)
  • Perhaps something like this:

    void Bar<T>()
    {
       if(typeof(T) == typeof(Stream))
          Foo(default(T) as Stream);  //just to show the scenario
    }
    
    0 讨论(0)
  • 2021-02-13 11:08

    default(T) will always return null when type T is of reference type and will return zero if T is of numeric value types.

    So at any time its not returning an object using which you can call your overloaded version of Foo methods.

    So Short answer you cant do this, you have to find out other ways to call overloaded methods.

    0 讨论(0)
提交回复
热议问题