1

we've reached a point where we have no clue how to continue:

SHORT: We have a generic interface and a collection of the generic interface. Trying to add an implementation of the generic interface to the collection fails. What happens is that I get a compile time exception saying:

cannot convert from TestApp.IState<T>' to TestApp.IState<TestApp.IView>'

LONG [Code example]:

class Program
{
    static void Main(string[] args)
    {
        var coll = new StateCollection();
        var state = new SomeState();
        coll.AddState(state);
    }
}

public class StateCollection
{
    private List<StateBase<IView>> _states = new List<StateBase<IView>>();

    public void AddState<T>(StateBase<T> state) where T: IView
    {
        _states.Add(state);
    }
}

public class SomeState : StateBase<SomeView>
{

    public IView View
    {
        get;
    }
}

public class SomeView : IView
{
}

public abstract class StateBase<T> where T : IView
{
    private SomeView _view;
    public SomeView View
    {
        get { return _view; }
    }
}

public interface IView
{
}

Why does this happen? In the AddState we mention that T has to be an instance of IState. Could someone help us out with why this happens and how to do what we want to do?

EDIT1: We also tried:

    public void AddState(IState<IView> state)
    {
        _states.Add(state);
    }

But that just moves the compile time error to 'coll.AddState(state)' So the same thing happens in another place.

EDIT2: PROBLEM! I didn't give the right example. Out IState is not an interface but an abstract class. Very sorry for that! Changed code to use abstract class

10
  • 1
    What version of C# are you using? C# < 3.0 doesn't have variance with generics. Commented Jan 18, 2013 at 15:56
  • This is because IState<SomeView> is not IState<IView>> Commented Jan 18, 2013 at 15:57
  • _states is defined as <IState<Iview>> but your trying to add IState into that collection. Commented Jan 18, 2013 at 15:58
  • @sll SomeView does implement IView Commented Jan 18, 2013 at 15:59
  • @Oded I'm using .NET version 4.0 Commented Jan 18, 2013 at 15:59

5 Answers 5

2

How about these changes:

public class SomeState : IState<SomeView>
{
    public SomeView View
    {
        get;
        set;
    }
} 

Instead of using generic Type T use IView in AddState method

public void AddState(IState<IView> state)
{
    _states.Add(state);
}

Make T covariant in IState using out keyword

public interface IState<out T> where T : IView
{
    T View { get; }
}

SOLUTION FOR EDIT2:

Don't know if it is ok for you but you can.

public class StateCollection
{
    private List<IState<IView>> _states = new List<IState<IView>>();

    public void AddState(IState<IView> state)
    {
        _states.Add(state);
    }
}

public class SomeState : StateBase<SomeView>
{
    public override SomeView View
    {
        get { return null; }
    }
}

public abstract class StateBase<T> : IState<T> where T : IView
{
   public abstract T View { get; }
}

public interface IState<out T> where T : IView
{
    T View { get; }
}
Sign up to request clarification or add additional context in comments.

1 Comment

What you are asking for is Covariance and Contravariance in Generics which is only applicaple for interfaces and delegates. The only chance is extracting your abstract class to an interface.
2

First solution

public class StateCollection
    {
        private readonly List<IState<IView>> _states = new List<IState<IView>>();

        public void AddState(IState<IView> state)
        {
            _states.Add(state);
        }
    }

as suggested by Nevyn. To have it to work, mark T as covariant in interface IState<T>

public interface IState<out T> where T:IView
    {
        IView View { get; }
    }

Second solution : (keep change in class StateCollection)

change interface IState<T> to

public interface IState<out T> where T : IView
    {
        T View { get; }
    }

and class SomeState to

public class SomeState : IState<SomeView>
    {
        public SomeView View{ get;private set; }
    }

Solution for Edit2 :

class Program
    {
        static void Main(string[] args)
        {
            var coll = new StateCollection();
            var state = new SomeState();
            coll.AddState(state);
            Console.ReadKey();
        }
    }

    public class StateCollection
    {
        private List<IStateBase<IView>> _states = new List<IStateBase<IView>>();

        public void AddState(IStateBase<IView> state)
        {
            _states.Add(state);
        }
    }

    public class SomeState : StateBase<SomeView>
    {
    }

    public class SomeView : IView
    {
    }

    public interface IStateBase<out T> where T : IView
    {
        T View { get; }
    }

    public abstract class StateBase<T> : IStateBase<T> where T : IView
    {
        public T View { get; set; }
    }

    public interface IView
    {
    }

2 Comments

@MrSoundless then... indeed, it won't work, as you can't use covariance outside interfaces and delegates.
@MrSoundless well see "Solution for Edit2" for a solution, maybe.
1

this looks more like a parameter error for the Add function. Have you tried declaring the add function without using the generics? The inheritance itself should allow it. Make the AddState function look like like so:

Edit (as per Edit2):

As mentioned, the inheritance itself should take care of the generics. As long as whatever class you declared properly implements IView, or IState<IView>, then there shouldn't be any issues...

public absract class StateBase
{
    public IView view { get; set; }

    ....
}

public Interface IView
{ ... }

public class StateCollection
{
    private List<StateBase> _states = new List<StateBase>();

    public void AddState(StateBase state)
    {
        _states.Add(state);
    }
}

public class SomeView : IView
{ ... }

etc etc and so on, as often as needed

public class SomeState : StateBase
{
    private SomeView my_view;

    public IView view
    {
        get { return (IView)SomeView; }
        set { ; }
    }
}

//program remains unchanged

In this case, SomeState is still an IState object, and all IState objects implement IView, and SomeView is an IView object. SomeState implements SomeView internally. Looks the same to me, but I dont know how well the adaption would work with your real code.

Any other classes would follow the same model. The State will implement StateBase, and internally declare a custom View, which itself needs to extend IView. That way the IView cast on the custom view will work.

From comment:

public class BarState : StateBase
{
    private BarView my_view;

    public IView view
    {
        get { return (IView)BarView; }
        set { ; }
    }
}

public class BarView : IView
{ ... }

7 Comments

I was editing the post at the same time you posted this issue. This also doesn't work. When I do this I just get "The best overloaded method match for 'TestApp.StateCollection.AddState(TestApp.IState<TestApp.IView>)' has some invalid arguments" when calling AddState (see EDIT1)
I tried state as IState<IView> but that just returns null as in, the casting fails
if the casting fails, then that means that the code doesn't recognize SomeState as a proper instance of IState<IView>, otherwise you would be able to cast it. This seems to be the root of the issue, inheritance isn't working as it looks like it should with Templating.
Yeah that seems to be the problem, but why and how do we get around this?
The real question I have at the moment: Would removing the templating break any other existing code, as long as the assumption that IView and StateBase and their relationship remains intact?
|
0

Add interface IState (non-generic) and inherit it from IState<T>. Then declare _states as List<IState> and method AddState(IState state)

Comments

0

(EDIT: you just need to have your StateBase inherit from the covariant interface. You can't make a class directly covariant, you always have to go through an interface)

Try this:

public class StateCollection
{
    private List<IState<IView>> _states = new List<IState<IView>>();

    public void AddState(IState<IView> state)   
    {
        _states.Add(state);
    }
}

public class SomeState : StateBase<SomeView>
{
}

public class SomeView : IView
{
}

public interface IState<out T> where T : IView // now covariant with T
{
    T View { get; }
}

public abstract class StateBase<T> : IState<T> where T : IView
{
    public T View { get; set; }
}

public interface IView
{
}

1 Comment

@MrSoundless see edit to answer. If you can change the definition for your abstract class (adding inheritance from a covariant interface) you can still make it work

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.