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.
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
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With