1

I have a function that is declared like so:

public static string MultiWhereToString(List<WhereCondition<T>> whereConditions)

I am trying to pass it a variable called whereAnd which is delcared like so:

private List<WhereAndCondition<T>> whereAnd = new List<WhereAndCondition<T>>();

WhereAndCondition is a sub class of WhereCondition. It is declared like so:

public class WhereAndCondition<T> : WhereCondition<T>, IConditional where T : DatabaseObject

My issue is, if I try to execute the following code:

private List<WhereAndCondition<T>> whereAnd = new List<WhereAndCondition<T>>();
MultiWhereToString(whereAnd);

I get the following error:

Error 3 Argument 1: cannot convert from 'System.Collections.Generic.List<BrainStorm.WhereAndCondition<T>>' to 'System.Collections.Generic.List<BrainStorm.WhereCondition<T>>'

Any ideas on why? I think it has to do with the generics of the WhereCondition classes.

Michał Powaga
  • 22,561
  • 8
  • 51
  • 62
user489041
  • 27,916
  • 55
  • 135
  • 204

6 Answers6

2

Given:

class A {}
class A : B {}

An object of List<B> is not an instance of List<A>. So you can't cast a List<WhereAndCondition> to a List<WhereCondition>. You could use:

MultiWhereToString(whereAnd.OfType<WhereCondition>().ToList());

(There might also be a solution involving the in and out variance annotations, but I'm not terribly familiar with them.)

millimoose
  • 39,073
  • 9
  • 82
  • 134
2

Your function is defined as taking a WhereCondition List, but you're passing it a WhereAndCondition List:

MultiWhereToString(List<WhereCondition<T>> whereConditions)

private List<WhereAndCondition<T>> whereAnd = new List<WhereAndCondition<T>>(); 
MultiWhereToString(whereAnd); 

List variance has limited supported in .NET 4. See this question.

Community
  • 1
  • 1
Esoteric Screen Name
  • 6,082
  • 4
  • 29
  • 38
  • The reason is the fact the cast is simply not typesafe – where does loss of data come into it? – millimoose Nov 15 '11 at 20:40
  • @Inerdia - I realized that as soon as I posted; I had a brain fart and was thinking of converting a parent to child, rather than the other way around. Edited with link to a better explanation than I could easily provide. And I realize that 'data loss' isn't really the correct term for that either. My language skills are garbage. – Esoteric Screen Name Nov 15 '11 at 20:43
2

Generics have to be known explicitly at compile time because they are generated.

Why not use:

private List<WhereCondition<T>> whereAnd = new List<WhereCondition<T>>();

So you can still add WhereAndCondition objects to whereAnd.

Tad Donaghe
  • 6,625
  • 1
  • 29
  • 64
Kay
  • 12,918
  • 4
  • 55
  • 77
2

You can replace the entire WhereCondition<T> in your MultiWhereToString method with another generic type which is restricted to WhereCondition<T>.

Replace:

public static string MultiWhereToString(List<WhereCondition<T>> whereConditions)

With:

public static string MultiWhereToString<TType>(List<TType> whereConditions) where TType: WhereCondition<T>

Or alternatively change:

private List<WhereAndCondition<T>> whereAnd = new List<WhereAndCondition<T>>();

to:

private List<WhereCondition<T>> whereAnd = new List<WhereCondition<T>>();

And let inheritance take care of the rest for you.

Johannes Kommer
  • 6,401
  • 1
  • 39
  • 45
2

This appears to be a covariance / contravariance issue.

Simplified to this:

    public class WhereCondition
    {
    }

    public class WhereAndCondition : WhereCondition
    {
    }

    public class blah
    {
        public static void Blah()
        {
            List<WhereAndCondition> whereAnd = new List<WhereAndCondition>();
            MultiWhereToString(whereAnd);
        }

        public static string MultiWhereToString(List<WhereCondition> whereConditions)
        {
            return null;
        }
    }

It's not going to work, because the list of WhereAndConditions can't be cast to List of WhereConditions:

Imagine it this way. You've got a list of giraffes, and the method is asking for a list of animals.

Without knowing what they are going to do with the list animals (like try adding a horse) the types are incompatible, but if you change it to something like this:

        public static string MultiWhereToString(IEnumerable<WhereCondition> whereConditions)
        {
            return null;
        }

Then the variance can kick in, and give you what you're looking for.

McKay
  • 12,334
  • 7
  • 53
  • 76
2

I would suggest using interfaces:

public static string MultiWhereToString(IEnumerable<ICondition<T>> whereConditions)

This would allow you a lot more freedom when calling this method.

Joe
  • 80,724
  • 18
  • 127
  • 145