views:

97

answers:

2

I have a view

ViewUserControl<SearchViewData>

where

SearchViewData: CommonViewData

In this view I thus have a reference to Html.
This is of the type HtmlHelper<SearchViewData>

I create a custom HtmlHelper class called CommonHtmlHelper where I want to this (note the HtmlHelper parameter's type):

 public static SelectList TranslatedApplicationSelectList(this HtmlHelper<CommonViewData> htmlHelper, string selectedCode)

Since SearchViewData inherits from CommonViewData and my Html is of type HtmlHelper<SearchViewData> it thus is also HtmlHelper<CommonViewData>.
Yet when I try to access the TranslatedApplicationSelectList method it in my views I get an error saying that HtmlHelper<SearchViewData> is not assignable to HtmlHelper<CommonViewData>.

Is this a flaw in my OO logic? Is this a limitation of how C# handles inheritance in generics (I think Skeet once explained me this, but I can't find the post anymore)?

and most of all, how do I fix it?

+2  A: 

.NET 3.5 does not handle Covariance and Contravariance, so the only compatible generic types in .NET 3.5 are those with the exact same type.

One example of why this should be allowed:

public class BaseType { }
public class DerivedType : BaseType { }

public void DumpList(List<BaseType> list)
{
    foreach (var o in list)
        Debug.WriteLine(o);
}

...
List<DerivedType> objects = new List<DerivedType>();
objects.Add(new DerivedType());
DumpList(objects);

Here we simply dump the contents (ie. read) from the objects, which is supposed to be safe.

A better example of why this is not allowed:

public class BaseType { }
public class DerivedType : BaseType { }

public void ManipulateList(List<BaseType> list)
{
    list.Add(new BaseType());
}

...
List<DerivedType> objects = new List<DerivedType>();
objects.Add(new DerivedType());
ManipulateList(objects);

Here we're trying to add an object of BaseType to a list that has been declared as storing DerivedType objects. This is not allowed.

Since the compiler is not able to distinguish between the two cases, they are all disallowed, because of that second case above.

In .NET 4.0 and C# 4.0 there will be extra syntax you can apply that specifies in what direction you're allowed to convert a generic type, depending on what you intend to do with the objects. This might allow the first of the two examples to work, but in .NET 3.5, this is not possible. The second example will not work in C# 4.0.

Here's an article by Charlie Calvert about Covariance and Contravariance in C# 4.0.

Lasse V. Karlsen
Yes, those were the things explained to me earlier by John Skeet. I had a feeling this was the reason of my problem, but wasn't sure and didn't know the terms so couldn't find the question again.So the only solution to my problem would be to create a method that accepts the HtmlHelper<SearchViewData> and write another one if I want to do it on another type :SWouldn't a syntax to "Cast" your generic type also be acceptable? It would put the responsibility at the programmer, just like the regular cast.
borisCallens
A: 

If your implmentation would otherwise allow you to use an HtmlHelper<SearchViewData> where you are currently using an HtmlHelper<CommonViewData>, then you should be able to declare your extension method like this instead:

public static SelectList TranslatedApplicationSelectList<T>(this HtmlHelper<T> htmlHelper, string selectedCode) where T : CommonViewData
kvb
That's indeed what I'm doing, but this would mean that I can't reuse the method in other views where other subclasses are used as viewdata.
borisCallens
@boris - I'm not sure that I understand why that would be the case - because the extension method would be generic, you could use it with any subclass...
kvb