Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a clever way to call a type dependent extension method dynamically?

Tags:

c#

linq

The code below will compile but fails at runtime. It's provided just to give an idea of what I'm trying to do. What I would like to do is create a method that accepts a collection of objects (effectively an "untyped" collection) and if this collect is comprised of numbers of a single type return the mean using the Average() extension method

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace nnConsole {
    class Program {
        static void Main(string[] args) {
            var ints = new object[4] { 1, 2, 3, 4 };
            var dbls = new object[4] { 1.0, 2.0, 3.0, 4.0 };
            Console.WriteLine(ReallyMean(ints));    
            Console.WriteLine(ReallyMean(dbls));    
            }

        static public double ReallyMean(ICollection<object> data) {
            var unique = data.Distinct();
            var types = unique.Select(x => x.GetType()).Distinct();
            if (types.Count() == 1) {
                if (types.First().IsValueType) {
                    /***********************
                     * magic here to create ddata var that will
                     * call the appropriate extension method for average */ 
                    dynamic ddata = data; // DOES NOT WORK
                    return ddata.Average();
                    }
                }
            return double.NaN;
            }
        }
    }

I'm sure I could use reflection to find the appropriate Average method and invoke it (not pretty). Is there a shorter/cleaner/better way to do this? I can use the latest C# and .NET runtime.

like image 746
Dweeberly Avatar asked Dec 31 '13 20:12

Dweeberly


2 Answers

Since your code always returns a double, what you are looking for is a lambda that translates your object to a double:

var res = data.Cast<dynamic>().Average(n => (double) n);

Note that your program would also compile without a Cast<dynamic>(), but it would fail at runtime unless the underlying data type is double.

like image 160
Sergey Kalinichenko Avatar answered Oct 24 '22 00:10

Sergey Kalinichenko


Calling ddata.Average() will cause the dynamic runtime to try to find a proper member method on ICollection<object>. It doesn't do the translation to a static method call that an extension method really is underneath the syntactic sugar.

You can however rewrite it to call the extension method with normal syntax. That will allow dynamic overload resolution if there are many matching extension methods in the same class, but it will not allow automatic selection of overloads from different classes:

return Enumerable.Average(ddata);

That will bind to the correct overload of Average depending on the compile time type of the IEnumerable<T>. You are now using IEnumerable<object>, so I guess you will have to change that to IEnumerable<dynamic> to get it right.

like image 42
Anders Abel Avatar answered Oct 24 '22 01:10

Anders Abel