Morgan Leppink Morgan Leppink - 2 months ago 6
C# Question

How do I cast the results of a LINQ query against my custom object implicitly?

I'm working on a project that uses lots of custom classes, each of which represents, effectively, a row of data from the database. I've been doing this when there is more than one than one data element:

public class Client
{
private int _clientID;
private double _annualRevenue;

public int ClientID
{
get { return _clientID; }
set { _clientID = value; }
}

public double AnnualRevenue
{
get { return _annualRevenue; }
set { _annualRevenue = value; }
}
}


And then, elsewhere in the code...

public class QueryClients
{

private List<Client> _clients;

public List<Client> FindClientByRevenue(double minimumRevenue)
{
List<Client> returnValue = (from c in _clients
where c.AnnualRevenue >= minimumRevenue
select c).ToList();

return returnValue;
}

}


This works fine, but IMHO it's ugly to have to keep declaring this list like this everywhere else in the code. I'm going to have to use this in at least 100 different places. I'd like to clean it up by creating a custom class to simply represent that List, using a meaningful name. What I mean is something LIKE this:

public class Clients : List<Client> { }


Now this works, but the results of that LINQ query will not cast to it IMPLICITLY. So bear with me. If the new code looks like this...

public class QueryClients
{

private Clients _clients;

public QueryClients(Clients clients)
{
_clients = clients;
}

public Clients FindClientByRevenue(double minimumRevenue)
{
Clients returnValue = (from c in _clients
where c.AnnualRevenue >= minimumRevenue
select c).ToList();

return returnValue;
}

}


...I get a compiler error:

CS0266
Cannot implicitly convert type 'System.Collections.Generic.List<Client>' to 'Clients'. An explicit conversion exists (are you missing a cast?)


Okay, okay, so I can disappear this compiler error in the IDE by EXPLICITLY casting the result of the LINQ query:

public class QueryClients
{

private Clients _clients;

public QueryClients(Clients clients)
{
_clients = clients;
}

public Clients FindClientByRevenue(double minimumRevenue)
{
Clients returnValue = (Clients)(from c in _clients
where c.AnnualRevenue >= minimumRevenue
select c).ToList();

return returnValue;
}

}


The code even compiles fine. But as soon as I hit that query, it blows up.

Clients clients = new Clients();
Client client;

client = new Client();
client.ClientID = 1;
client.AnnualRevenue = 100;
clients.Add(client);

client = new Client();
client.ClientID = 2;
client.AnnualRevenue = 200;
clients.Add(client);

client = new Client();
client.ClientID = 3;
client.AnnualRevenue = 300;
clients.Add(client);

QueryClients q = new QueryClients(clients);

Clients clients2 = q.FindClientByRevenue(200);


Of course, it's a casting error.

An unhandled exception of type 'System.InvalidCastException' occurred.

Additional information: Unable to cast object of type 'System.Collections.Generic.List`1[Client]' to type 'Clients'.


So even though it compiles, it won't RUN, and even if this approach DID WORK, it doesn't really solve my problem because then I'd have to explicitly cast the results of every LINQ query and if I have to do that I might as well just declare lists everywhere. Sigh.

So anyway, I'm doing something wrong, and I just can't see how to do fix it. I'm sure that, at minimum, I need to add a conversion/casting operator of some sort to my Clients class to allow an implicit conversion to happen (or something). I've tried numerous different things based on numerous different Google searches and it either won't compile or it won't run. I just can't figure it out.

There are two goals:

1.) Use a Clients object in my code rather than having to declare a list variable over and over.

2.) Be able to run a LINQ query to select some Client object(s) from one Clients object into another (new) Clients object.

Is this possible? Can you actually show me how to modify the Clients class so it will work (casting implicitly)? If it can be done, I'm sure it's simple and I'll kick myself for not having seen it myself before posting.

Thanks in advance for the insight.

Answer

You get a cast exception because the dynamic type of the result of the list query is List<Client> and not Clients which inherits from it (You can't cast from an 'Animal' to a 'Dog' when the object is not actually a 'Dog').

Since you cannot have any user defined conversions from a base class I would suggest removing the inheritance and adding an implicit (or explicit cast):

public class Clients {
    private List<Client> d;

    public Clients(List<Client> d)
    {
        this.d = d.ToList();
    }

    public static implicit operator Clients(List<Client> d)
    {
        return new Clients(d);
    }
}

This however will not allow you to treat Clients as an Enumerable. Alternatively you may write an extension method to IEnumerable<Client> with the following signature:

  public static Clients ToClientList(this IEnumerable<Client> input){
     return new Clients(input);
  } 

As a side note, I think that using a List<Client> is more elegant and clear than defining your own class..

Comments