0

What is the correct syntax for a method to receive this list as an argument?

var customList = await db.MyDbTable
    .Select(x => new { x.Id, x.ParentId, x.Title })
    .ToListAsync();
MyMethod(customList);

This doesn't work...

private void MyMethod(List<int, int, string> inputList)
{
    // process the input list
    return;
}
Stian
  • 1,522
  • 2
  • 22
  • 52
  • 4
    You can't pass an anonymous type to a method. You need to create an actual type instead. – stuartd Feb 26 '20 at 23:08
  • 2
    Ref: [How to pass anonymous types as parameters?](https://stackoverflow.com/questions/6624811/how-to-pass-anonymous-types-as-parameters) – stuartd Feb 26 '20 at 23:13
  • 1
    Lists don't have “columns”. – Ondrej Tucny Feb 26 '20 at 23:13
  • 1
    @stuartd Really?! I was certain it had to be possible. My attempt seemed so neat... Ok, so I'll just make a new POCO-class and use that, then? – Stian Feb 26 '20 at 23:14
  • @stuartd - How about dynamic as function parameter. Then one can pass Anon types. – Prateek Shrivastava Feb 26 '20 at 23:19
  • @PrateekShrivastava you can use dynamic, as one of the answers in the link shows. My preferred solution would be an actual class 100% of the time - as the method being called is private, it can be a private inner class. I have worked with code that uses dynamic in the past and I still use it - but only when I absolutely have to. I like it when the compiler tells me something I have done is wrong, rather than an angry customer. – stuartd Feb 26 '20 at 23:21
  • Or you could try named tuples https://stackoverflow.com/q/45631769/8024897 – ZiggZagg Feb 26 '20 at 23:32

4 Answers4

7

A lot of things are wrong here. I am guessing you are quite new to c#. I will try to explain as much as possible -

First,

var customList = await db.MyDbTable
.Select(x => new { x.Id, x.ParentId, x.Title })
.ToListAsync();

customList is not a collection of <int, int, string>. The syntax -

x => new { x.Id, x.ParentId, x.Title }

means, x is an anonymous object that has 3 properties named Id, ParentId, Title.

Second,

The syntax used is a shortcut. The actual syntax will give you a much more clear picture -

x => new <anonymous object> { Id = x.Id, ParentId = x.ParentId, Title = x.Title }

Third,

Because of what I mentioned at (second), the type definition of the list is this -

List<object> customList = await db.MyDbTable
.Select(x => new { x.Id, x.ParentId, x.Title })
.ToListAsync();

Which is clearly not List<int, int, string> and will definitely not work. In fact list does not even support List<int, int, string>. List only takes one data type (List<>) not three (List<,,>). More details here - https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1?view=netframework-4.8

Fourth,

Since the list is of type List<object>, inside the function, the compiler does not know that the object contains Id, ParentId and Title. There are a lot of approach to fix that, you could use class or dynamic objects. The good approach is the class -

public class Data {
    public int Id {get;set;}
    public int ParentId {get;set;}
    public string Title {get;set;}
}

List<Data> customList = await db.MyDbTable
.Select(x => new Data { Id = x.Id, ParentId = x.ParentId, Title = x.Title })
.ToListAsync();

private void MyMethod(List<Data> inputList)
{
    // process the input list
    return;
}

or you could use value tuple. Read about them here - https://learn.microsoft.com/en-us/dotnet/csharp/tuples -

var customList = await db.MyDbTable
.Select(x => (Id: x.Id, ParentId: x.ParentId, Title: x.Title ))
.ToListAsync();

private void MyMethod(List<(int Id, int ParentId, string Title)> inputList)
{
    //example 
    var item = inputlist.First())
    var id = item.Id;

    // process the input list
    return;
}

or you could use reflection since you already know that object has the properties. But this type of coding is not recommended and you should avoid. But still I am going to show how to use -

var customList = await db.MyDbTable
.Select(x => new { x.Id, x.ParentId, x.Title })
.ToListAsync();

private void MyMethod(List<object> inputList)
{
    //example 
    var item = inputlist.First())
    var id = item.GetType().GetProperty("Id").GetValue(item) //but a lot slower and not recommended
    // process the input list
    return;
}
RoadRunner
  • 25,803
  • 6
  • 42
  • 75
brainless coder
  • 6,310
  • 1
  • 20
  • 36
3

You could create a class:

public class MyClass
{
    public int Id { get; set; }
    public int ParentId { get; set; }
    public string Title { get; set; }
}

Then instead of creating an anonymous type, use this class instead:

var customList = await db.MyDbTable
    .Select(x => new MyClass { 
        Id = x.Id, 
        ParentId = x.ParentId, 
        Title = x.Title 
    })
    .ToListAsync();

And your method would turn into:

private void MyMethod(List<MyClass> inputList) # or IEnumerable<MyClass>
{
    // process the input list
    return;
}

As @stuartd points out in the comments, you could also make MyClass a private class inside your main class since its only used for a private method:

public class Program {

    private class MyClass
    {
        public int Id { get; set; }
        public int ParentId { get; set; }
        public string Title { get; set; }
    }

    // Rest of code
}

This however depends on how the rest of your code is implemented.

RoadRunner
  • 25,803
  • 6
  • 42
  • 75
2

Since no one's mentioned it yet, you could also use a Tuple:

var customList = await db.MyDbTable
    .Select(x => Tuple.Create(x.Id, x.ParentId, x.Title))
    .ToListAsync();
MyMethod(customList);

...

private void MyMethod(List<Tuple<int, int, string>> inputList)
{
    // process the input list
    foreach (var item in inputList)
    {
        var id = inputList[0].Item1;
        var parentId = inputList[0].Item2;
        var title = inputList[0].Item3;
    }
    return;
}

Or a ValueTuple in C# 7+:

var customList = await db.MyDbTable
    .Select(x => (x.Id, x.ParentId, x.Title))
    .ToListAsync();
MyMethod(customList);

...

private void MyMethod(List<(int, int, string)> inputList)
{
    // process the input list (same as above)
    return;
}
p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
0

Well This will work, BUT - this is susceptible to exceptions if you goof up with what goes as dynamic parameter.

static void Main()
{
    var src = new List<int>();

    for (int i = 0; i < 200; i++)
    {
        src.Add(i);
    }

    var linqRetval = src.Select(x => new
    {
        Id = x,
        First = $"Hello{x}",
        Second = $"World{x}"
    }).ToList();

    SomeFunction(linqRetval);
}

static void SomeFunction(dynamic input)
{
    foreach (var item in input)
    {
    }
}
Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Prateek Shrivastava
  • 1,877
  • 1
  • 10
  • 17