C# generics: failure of generic type inference means I cannot use anonymous type
I have a class Exporter which has a generic method which accepts an IEnumerable<T>
and creates an export document by enumerating its property values using reflection:
public class Exporter
{
public string Export<T>(IEnumerable<T> enumerable)
{
//Implementation omitted
}
}
Because of generic type inference, I can supply this with an anonymous collection type. Note the absence of the generic parameter in the method call below:
string fooString =
new Exporter().Export(new List<Foo>()
{
new Foo() {Name = "cats", NumberOfHams = 1},
new Foo() {Name = "dogs", NumberOfHams = 8}
}
.Select(x => new { TwiceTheHams = x.NumberOfHams * 2 }));
We all love C#3. However, I would like to adapt this class so that I can enter more information about the columns in the export document, for example the width.
I have created a new method in the export class which looks like this:
public string Export<T>(IEnumerable<T> enumerable, IEnumerable<ColumnDetails<T>> columnDetails)
{
//Implem开发者_如何学编程entation omitted
}
Ideally, the syntax would be like this, where foos
is of type IEnumerable<Foo>
:
fooString = new Exporter().Export(foos,
new List<ColumnDetails<Foo>>
{
new ColumnDetails<Foo>(x => x.Name, 12),
new ColumnDetails<Foo>(x => x.NumberOfHams, 4),
});
However, when I call the new Export()
overload as above, the generic type inference doesn't seem to be clever enough to infer that the generic parameter T
for the ColumnDetails<T>
should be the same as the generic parameter T
for the IEnumerable
. This means that I must specify List<ColumnDetails<Foo>>
as the parameter, and therefore I cannot use this with anonymous collections.
I'm really new to generics and type inference. Is what I'm trying to do possible? Do I need to restructure the code somehow?
Edit: this is what I cannot do, because Visual Studio needs the generic parameter for ColumnDetails, which I don't have:
fooString = new Exporter().Export(foos.Select(x => new {TwiceTheHams = x.NumberOfHams * 2}),
new List<ColumnDetails>
{
new ColumnDetails(x => x.TwiceTheHams, 12)
});
Would this be sufficient to fix it?
Export<Foo>
fooString = new Exporter().Export<Foo>(foos,
new List<ColumnDetails<Foo>>
{
new ColumnDetails<Foo>(x => x.Name, 12),
new ColumnDetails<Foo>(x => x.NumberOfHams, 4),
});
How about something like this?
The anonList
lets you have a reference IEnumerable<anontype>
and the T foo
in CreateColumnDetails<T>
lets the compiler infer what type T is, allowing you to construct the object with an anonymous type as the value of T
class Program
{
public static ColumnDetails<T> CreateColumnDetails<T>(T foo, Func<T, object> func, int x)
{
return new ColumnDetails<T>(func, x);
}
static void Main(string[] args)
{
IEnumerable<Foo> foos = new List<Foo>();
var anonList = foos.Select(x => new {TwiceTheHams = x.NumberOfHams*2});
var fooString = new Exporter().Export(anonList,
anonList.Select(y => CreateColumnDetails(y, z => z.TwiceTheHams, 12)));
}
}
public class Exporter
{
public string Export<T>(IEnumerable<T> enumerable, IEnumerable<ColumnDetails<T>> columnDetails)
{
return string.Empty;
}
}
public class ColumnDetails<T>
{
public ColumnDetails(Func<T, object> func, int x)
{
}
}
public class Foo
{
public string Name { get; set; }
public string NumberOfHams { get; set; }
}
So you want to pass in a List<ColumnDetails< *anonymouse type with TwiceTheHams property* >>
?
It looks to me like you need a generic helper function that will take an IEnumerable<T>
parameter and construct a list of ColumnDetails
objects from it. Then insert a call to that function as the second parameter to Export
.
精彩评论