Generic constraints: compiler errors on conversion that should bevalid

H

Harold Howe

Howdy all,

I am getting a compiler error regarding a consrained conversion. It
complains that it can't make the type conversion, even though the
generic type argument inherits from the target of the conversion. I have
trimmed my source down as much as possible.

The classes implement something that behaves sort of like the Mediator
design pattern, but where the colleagues are abstract, and can be added
or removed on the fly. I guess that makes this more like the Observer
pattern. Either way, the base classes are designed to handle the plumbing.

The idea is that there is a mediator object that contains a list of view
objects. When something of interest happens in the mediator, it notifies
the views.

// main.cs
using System;
using System.Collections.Generic;

//---------------------------------------
// base types to handle plumbing
public interface IMediator<TView> //: IList<T>
{
// In reality, this interface inherits from IList<T>, but
// I removed that inheritance for brevity.
void Add(TView item);
void Clear();
}

public interface IBaseView<TMediator, TView>
where TMediator : IMediator<TView>
{
// All views have a Mediator property.
TMediator Mediator { get;set;}
}

public class BaseView<TMediator, TView> : IBaseView<TMediator, TView>
where TMediator : IMediator<TView>
{
public BaseView() {}

public TMediator Mediator
{
get { return m_Mediator; }
set { m_Mediator = value; }
}

protected TMediator m_Mediator;
}


public class Mediator<T> : IMediator<T>
where T : IBaseView<Mediator<T>, T>
{
protected List<T> m_List = new List<T>();

public virtual void Add(T item)
{
m_List.Add(item);
item.Mediator = this;
}
public virtual void Clear()
{
foreach(T t in m_List)
t.Mediator = null;
m_List.Clear();
}
}

//---------------------------------------
// concrete types
public interface IView : IBaseView<Mediator, IView>
{
void ZoomChanged();
}

public class ConcreteView : BaseView<Mediator, IView>, IView
{
public virtual void ZoomChanged() {}
}

public class Mediator : Mediator<IView> // ERROR on this line
{
public void ZoomTo(float zoom)
{
// ... zoom the display
foreach(IView view in m_List)
view.ZoomChanged();
}
}

class Program
{
static void Main(string[] args)
{
Mediator med = new Mediator();
med.Add(new ConcreteView());

med.ZoomTo(.5f);
}
}

// compiler error
Error 1 The type 'IView' must be convertible to
'IBaseView<Mediator<IView>,IView>' in order to use it as parameter 'T'
in the generic type or method 'Mediator<T>'

The confusing thing is that IView inherits from
IBaseView<Mediator<IView>, IView>, so I don't understand why the
compiler complains about the conversion.

H^2
 
P

Peter Sestoft

Harold Howe said:
Howdy all,

I am getting a compiler error regarding a consrained conversion. It
complains that it can't make the type conversion, even though the
generic type argument inherits from the target of the conversion. I
have trimmed my source down as much as possible.

The classes implement something that behaves sort of like the Mediator
design pattern, but where the colleagues are abstract, and can be
added or removed on the fly. I guess that makes this more like the
Observer pattern. Either way, the base classes are designed to handle
the plumbing.

The idea is that there is a mediator object that contains a list of
view objects. When something of interest happens in the mediator, it
notifies the views.

// main.cs
using System;
using System.Collections.Generic;

//---------------------------------------
// base types to handle plumbing
public interface IMediator<TView> //: IList<T>
{
// In reality, this interface inherits from IList<T>, but
// I removed that inheritance for brevity.
void Add(TView item);
void Clear();
}

public interface IBaseView<TMediator, TView>
where TMediator : IMediator<TView>
{
// All views have a Mediator property.
TMediator Mediator { get;set;}
}

public class BaseView<TMediator, TView> : IBaseView<TMediator, TView>
where TMediator : IMediator<TView>
{
public BaseView() {}

public TMediator Mediator
{
get { return m_Mediator; }
set { m_Mediator = value; }
}

protected TMediator m_Mediator;
}


public class Mediator<T> : IMediator<T>
where T : IBaseView<Mediator<T>, T>
{
protected List<T> m_List = new List<T>();

public virtual void Add(T item)
{
m_List.Add(item);
item.Mediator = this;
}
public virtual void Clear()
{
foreach(T t in m_List)
t.Mediator = null;
m_List.Clear();
}
}

//---------------------------------------
// concrete types
public interface IView : IBaseView<Mediator, IView>
{
void ZoomChanged();
}

public class ConcreteView : BaseView<Mediator, IView>, IView
{
public virtual void ZoomChanged() {}
}

public class Mediator : Mediator<IView> // ERROR on this line
{
public void ZoomTo(float zoom)
{
// ... zoom the display
foreach(IView view in m_List)
view.ZoomChanged();
}
}

class Program
{
static void Main(string[] args)
{
Mediator med = new Mediator();
med.Add(new ConcreteView());

med.ZoomTo(.5f);
}
}

// compiler error
Error 1 The type 'IView' must be convertible to
'IBaseView<Mediator<IView>,IView>' in order to use it as parameter 'T'
in the generic type or method 'Mediator<T>'

The confusing thing is that IView inherits from
IBaseView<Mediator<IView>, IView>, so I don't understand why the
compiler complains about the conversion.

Generic types are not co-variant in their generic parameters, so
from

IView : IBaseView<Mediator, IView>
Mediator : Mediator<IView>

it does not follow that

IView : IBaseView<Mediator<IView>, IView>

Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top