By Rachel


2014-09-11 16:24:50 8 Comments

I have the following classes

public abstract class BaseViewPresenter { }
public abstract class BaseView<T> : UserControl
    where T : BaseViewPresenter { }

public class LoginPresenter : BaseViewPresenter { }
public partial class LoginView : BaseView<LoginPresenter> {  }

I have a method that looks like this (simplified)

public BaseView<BaseViewPresenter> Resolve(BaseViewPresenter model)
{
    var type = model.GetType();
    var viewType = _dataTemplates[type];

    // Correctly creates BaseView object
    var control = Activator.CreateInstance(viewType);

    // Fails to cast as BaseView<BaseViewPresenter> so returns null
    return control as BaseView<BaseViewPresenter>;
}

When I call this using an instances of LoginPresenter

var login = new LoginPresenter();
var ctl = Resolve(login);

The line Activator.CreateInstance(viewType) correctly resolves into a new instances of my LoginView, however control as BaseView<BaseViewPresenter> can't do the cast correctly so returns null.

Is there a way to correctly cast the control into BaseView<BaseViewPresenter> without using specific type generics?

Since LoginView inherits from BaseView<LoginPresenter>, and LoginPresenter inherits from BaseViewPresenter, I would assume there's a way to convert LoginView to BaseView<BaseViewPresenter>.

I am stuck with using .Net 3.5

3 comments

@Rachel 2014-09-11 18:53:07

I accepted Eric's answer since it provides a great explanation of why what I wanted wasn't possible, but I also thought I'd share my solution in case anyone else runs into this same problem.

I removed the generic type parameter from my original BaseView class, and created a 2nd version of the BaseView class that included the generic type parameter and specifics for it.

The first version is used by my .Resolve() method or other code that doesn't care about the specific types, and the second version is used by any code that does care, such as the implentation of a BaseView

Here's an example of how my code ended up looking

// base classes
public abstract class BaseViewPresenter { }
public abstract class BaseView : UserControl 
{
    public BaseViewPresenter Presenter { get; set; }
}

public abstract class BaseView<T> : BaseView
    where T : BaseViewPresenter
{
    public new T Presenter
    {
        get { return base.Presenter as T; }
        set { base.Presenter = value; }
    }
}

// specific classes
public class LoginPresenter : BaseViewPresenter { }
public partial class LoginView : BaseView<LoginPresenter> 
{
     // Can now call things like Presenter.LoginPresenterMethod()
}

// updated .Resolve method used for obtaining UI object
public BaseView Resolve(BaseViewPresenter presenter)
{
    var type = model.GetType();
    var viewType = _dataTemplates[type];

    BaseView view = Activator.CreateInstance(viewType) as BaseView;
    view.Presenter = presenter;

    return view;
}

@Andrew Hanlon 2014-09-17 18:01:38

You could use an extension method to get the desired functionality from your original question. Just create the extension method as: public static BaseView<T> Resolve<T>(this T presenter) where T: BaseViewPresenter And then cast to BaseView<T> instead of BaseView.

@Rachel 2014-09-17 18:10:31

@AndrewHanlon That won't work because I do not want to have to specify a specific type to use the .Resolve method. That's why my question specifies "without using specific type generics". Thank you though :)

@Andrew Hanlon 2014-09-17 20:34:46

If you used it in the same fashion as in your question (var login = new LoginPresenter; var ctrl = LoginPresenter.Resolve();) then it would indeed work correctly and return a BaseView<LoginPresenter> - no generic specification necessary.

@Rachel 2014-09-17 21:13:42

@AndrewHanlon Yes I'm sorry about that code sample, I used it for simplicity. In reality my .Resolve() method is called from the code-behind a custom UserControl, and the presenter passed into it is dynamic and unknown by the UserControl at the time it evalutes. All it cares about is it is of type BaseViewPresenter

@Arunprasanth K V 2016-09-08 13:45:02

perfect thanks for providing a good answer

@Eric Lippert 2014-09-11 16:38:45

This is a very frequently asked question. Let's rename your types:

abstract class Fruit { }                    // was BaseViewPresenter
abstract class FruitBowl<T> where T : Fruit // was BaseView
class Apple : Fruit { }                     // was LoginPresenter
class BowlOfApples : FruitBowl<Apple> {  }  // was LoginView

Your question now is:

I have a BowlOfApples, which inherits from FruitBowl<Apple>. Why can I not use it as a FruitBowl<Fruit>? An apple is a fruit, so a bowl of apples is a bowl of fruit.

No, it isn't. You can put a banana in a bowl of fruit, but you can't put a banana in a bowl of apples, and therefore a bowl of apples is not a bowl of fruit. (And by similar argument, a bowl of fruit is not a bowl of apples either.) Since the operations you can legally perform on the two types are different, they cannot be compatible.

Here is a photo of StackOverflow legend Jon Skeet demonstrating this fact:

enter image description here

The feature you want is called generic contravariance, and it is supported only on interfaces and delegate types when the compiler can prove that the variance is safe, and when the varying type is a reference type. For example, you can use an IEnumerable<Apple> in a context where IEnumerable<Fruit> is needed because the compiler can verify that there is no way that you can put a Banana into a sequence of fruit.

Do a search on "C# covariance and contravariance" on this site or on the web and you'll find many more details about how this feature works. In particular, my series of articles on how we designed and implemented this feature in C# 4 starts here: http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-contravariance-in-c-part-one.aspx

@Rachel 2014-09-11 16:43:32

Thank you, your analogy of using fruit actually makes the problem a lot clearer to me :)

@Eric Lippert 2014-09-11 16:46:45

@Rachel: You are welcome! Jon Skeet usually uses bowls, fruit, apples and bananas when answering this question; I usually use cages, animals, giraffes and tigers, which is more exciting. A paddock full of giraffes cannot be used as an animal cage because you could put a tiger into it, which would eat the giraffes. Which raises the question: would a tiger actually try to eat a giraffe? Giraffes kick pretty hard.

@Eric Lippert 2014-09-11 16:53:07

On the other hand, Jon can actually do a demonstration with fruit (see photo) and I cannot easily do so with giraffes.

@Eric Lippert 2014-09-11 16:57:47

@Rachel: One other thing: you have an intuitive understanding of the analogy, because the "generic type" in your mind that is "bowl of something" is very clear to you. The fact that the generic relationships in the business domain of your program are not immediately clear to you is evidence that you might not want to use generics for this purpose. I try to only use generics for cases where it is intuitively obvious what the generic type means. "baseview of some kind of presenter" is nowhere near as clear as "bowl of some kind of fruit", so maybe baseview shouldn't be generic at all.

@phoog 2014-09-11 17:05:30

@Rachel or maybe there is some subset of the baseview contract that (1) is what you actually need from the object returned and (2) does not need to be generic, while some implementation detail does need to be generic. Then you can pull those members up into a base class or interface, and use that type as the return type of Resolve. The more-derived generic type can have a reference to the specific type of view it is presenting without necessarily exposing it to the consumer of the method.

@phoog 2014-09-11 17:10:52

@Rachel for example, BaseView might have a method EatThePresenter() whose caller -- also the caller of Resolve -- does not need to know what presenter is being eaten, but the BaseView inheritor needs access to some property of the presenter to know how to eat it (i.e., does it first need to be peeled or sliced or... wait, did I say "presenter," I meant "fruit")

@Rachel 2014-09-11 17:13:54

@phoog My ultimate goal is to make the .Resolve method assign ((BaseView<BaseViewPresenter>)control).DataContext = model, and then return the UserControl object to be inserted in the UI.

@phoog 2014-09-11 21:01:29

@Rachel in that case you might be able to come up with something clever but it would probably be best to ask in a separate question. But unless I were looking for an exercise in generics (which I often am), I would be very likely just to cast the DataContext in the property setter or a set method -- you are already casting the object returned from Activator.CreateInstance. Casts are fairly cheap -- it's just a type check, really.

@Rachel 2014-09-12 00:22:22

@phoog Thanks, I actually did end up doing something similar and posted my own answer here :)

@BJ Myers 2017-05-03 16:44:08

I just found this today and it is my new favorite answer on SO.

@Servy 2014-09-11 16:28:59

You're expecting to treat the type as being covariant with respect to the generic argument. Classes can never be covariant; you'd need to use an interface rather than (or in addition to) an abstract class to make it covariant with respect to T. You'd also need to be using C# 4.0.

@Rachel 2014-09-11 16:33:44

LoginView does inherit from BaseView<LoginPresenter>, and LoginPresenter does inherit from BaseViewPresenter, so in theory I would assume that its possible to cast LoginView to BaseView<BaseViewPresenter>. You're saying that is not possible with 3.5 though?

@Servy 2014-09-11 16:38:27

@Rachel And that assumption would be incorrect. The type would need to be covariant with respect to its generic argument for that to be valid. Classes in C# are never covariant. Interfaces have the potential to be covariant. The syntax to support covariance for interfaces was added in C# 4.0.

@Alexei Levenkov 2014-09-11 16:39:17

+1. @Rachel - your expectation is simply unexpected. Relation between classes do not imply any relation between classes using them as generic's arguments. I.e. List<Base> and List<Derived> are siblings (have common base interface), but not derived from each other in any way. There are many posts on this topic - search for "C# covariance" (can add "Eric Lippert" for better results).

Related Questions

Sponsored Content

30 Answered Questions

[SOLVED] How to create a generic array in Java?

24 Answered Questions

[SOLVED] Get generic type of class at runtime

32 Answered Questions

[SOLVED] How do I generate a random int number?

  • 2010-04-24 23:09:11
  • Rella
  • 2140322 View
  • 1782 Score
  • 32 Answer
  • Tags:   c# random

12 Answered Questions

[SOLVED] How to create a new object instance from a Type

16 Answered Questions

[SOLVED] How to get the type of T from a member of a generic class or method?

  • 2009-02-17 15:24:36
  • Patrick Desjardins
  • 592298 View
  • 642 Score
  • 16 Answer
  • Tags:   c# .net generics

9 Answered Questions

[SOLVED] How to get the list of properties of a class?

7 Answered Questions

[SOLVED] How do I use reflection to call a generic method?

3 Answered Questions

[SOLVED] Can't cast derived type to base abstract class with type parameter

1 Answered Questions

[SOLVED] Usercontrols that inherit from abstract class

1 Answered Questions

Sponsored Content