NibblyPig
NibblyPig

Reputation: 52942

Pass List<anonymous type> into method requiring List<T> in C#

Is this possible?

var cats = catsource.Select(c => new
{
    CatId = c.catId,
    Name = c.Name,
}).ToList();

var myCatObject = new CatData(cats);

This doesn't work because the compiler says it can't convert cats into List<T>.

cats is an anonymous type, and CatData is:

public class CatData<T>
{
    public int total;
    public int records;
    public List<T> records;

    public CatData(List<T> dataObjects, int totalRecords, int numberOfRows)
    {
        total = (int)Math.Ceiling((double)totalRecords / numberOfRows);
        records = totalRecords;
        rows = dataObjects;
    }
}

Any ideas?


Edit:

So I have done this code, but there is still a problem:

 public class CatData
 {
     public int total;
     public int records;
     public List<T> rows;

     private CatData(List<T> dataObjects, int totalRecords, int numberOfRows)
     {
         total = (int)Math.Ceiling((double)totalRecords / numberOfRows);
         records = totalRecords;
         rows = dataObjects;
     }

     public static CatData<T> Create(List<T> dataObjects, int totalRecords, int numberOfRows)
     {
         return new CatData<T>(dataObjects, totalRecords, numberOfRows);
     }
 }

This code has an error on every instance of <T> saying

Cannot resolve symbol T

If I alter the class to be public class CatData<T> it compiles, but then I cannot have this code:

CatData.Create(records, totalRecords, rows);

Because it says

incorrect number of type parameters.


Update to explain what I want:

I want to create a list of objects which have an anonymous type

var someList = someCollection.Select(new { ... });

I want to create an object which stores them as a List<anonymous>

var myObject = new myObject(someList, 5);

So I can do:

myObject.someValue; // 5 (stored it in the constructor) myObject.myList.Count(); // can count how many anonymous objects are in there

Perhaps I should just store it as a List<object>.


I don't really get it, maybe I'm just a bit too thick. I've just ended up with this:

public CatData(List<object> dataObjects, int totalRecords, int numberOfRows) as my constructor.

var catData = new CatData(records.Cast<object>().ToList(), totalRecords, rows);

Upvotes: 3

Views: 1574

Answers (4)

Lasse V. Karlsen
Lasse V. Karlsen

Reputation: 391336

You need to define a factory method.

Generics are inferred (if possible) when calling methods, but not when calling constructors.

So add this:

public static class CatData
{
    public static CatData<T> Create<T>(List<T> cats)
    {
        return new CatData<T>(cats);
    }
}

used like this:

var data = CatData.Create(cats);

Eric Lippert has also provided an answer here talking about some of the considerations for why this is not allowed.

With the introduction of the new Roslyn compiler, new features might have a better chance of appearing as it would mean that 1 change can apply to multiple languages and would be easier to implement, which is probably what sparked new life into this discussion, as apparently supporting type inference on generic constructor parameters is now being considered: DamienG's blog, Probable C# 6.0 features illustrated


Since there seems to be some confusion as whether this trick actually works or not, here's a complete LINQPad program that demonstrates:

void Main()
{
    var test = new[]
    {
        new { name = "Test", id = 42 },
        new { name = "Test 2", id = 17 }
    }.ToList();
    CatData.Create(test);
}

public class CatData<T>
{
    public CatData(List<T> list)
    {
    }
}

public static class CatData
{
    public static CatData<T> Create<T>(List<T> list)
    {
        return new CatData<T>(list);
    }
}

This works just fine, illustrating that it is entirely legal to have both the static non-generic class and the generic class with the same class name, in the same namespace and assembly.

There should be ample evidence of this in the .NET framework as well, since Tuple exists with multiple versions.

Upvotes: 11

ach
ach

Reputation: 2373

You cannot make a constructor generic; and to instantiate a generic class you need to specify the class parameters.

But, there's a work-around:

public static class CatDataHelper
{
    public static CatData<T> MakeCatData<T>(this List<T> list)
    {
        return new CatData<T>(list);
    }
}

//...
var myCatObject = cats.MakeCatData();

Upvotes: 0

MarcinJuraszek
MarcinJuraszek

Reputation: 125630

Generic type inference works when you call methods, so you should create a factory method:

public static class CatData
{
    public static CatData<T> Create<T>(List<T> dataObjects, int totalRecords, int numberOfRows)
    {
        return new CatData<T>(dataObjects, totalRecords, numberOfRows);
    }
}

and now you can do:

var cats = catsource.Select(c => new
{
    CatId = c.catId,
    Name = c.Name,
}).ToList();

var myCatObject = CatData.Create(cats, cats.Length, cats.Length);

Upvotes: 2

Harikant
Harikant

Reputation: 277

Instead of using var use dynamic.

 dynamic cats = catsource.Select(c => new
 {
     CatId = c.catId,
      Name = c.Name,
 }).ToList();

Upvotes: -4

Related Questions