views:

163

answers:

5

Say that you're writing a library to display things on the screen, so you create an IDisplayable interface. This interface has one method to create a control from the object: displayable.GetControl().

You want to create your own list type that can be displayed: MyList<T>. Now this list can only be displayed if T is an IDisplayable, so you could ask in the MyList class that T should implement IDisplayable. But you also want to use this list type in some places when T is not IDisplayable (and as a result this list will not be displayable). So is it possible to say that MyList implements IDisplayable if T implements IDisplayable? I would also be happy if MyList<T> always implements IDisplayable but throws an exception at runtime if you try to call GetControl() if T is not IDisplayable, but I'd like to know if there's a statically type-safe way to do it. Can this be done? Or am I looking at the wrong solution?

Edit:

I agree with the suggestions so far that MyList may have too many responsibilities. My original idea was to create a MyDisplayableList<T> : MyList<T> (where T : IDisplayable).

The problem with this approach is that I have a lot of methods that take a MyList and return a MyList (for example methods like Select in Linq). So if I use select on an MyDisplayableList I get back a MyList and them I'm unable to display it even though it is a MyList...is there a type safe way to handle this problem in C#?

+5  A: 

Simple. Check if the type is IDisplayable. If it's not, throw an InvalidOperationException:

if (!typeof(IDisplayable).IsAssignableFrom(typeof(T))) 
    throw new InvalidOperationException();

Or if you have an instance of T, simply check with that:

IDisplayable disp = instanceOfT as IDisplayable;
if (disp == null)
    throw new InvalidOperationException();
// do stuff with `disp`.

Your design might be flawed though. You might be putting too much in a class and violating single responsibility principle. Recheck your design first.

Mehrdad Afshari
Thank you, that is what I'm going to use. But I'd prefer a statically type-safe approach if it's available...
Jules
There's no way to enforce that statically (for some specific methods of a class.) Type constraints are enforced at the type level.
Mehrdad Afshari
+8  A: 

It's not possible as you describe it. You should create two types of list :

public class MyList<T> : IList<T>
{
    ...
}

public class MyDisplayableList<T> : MyList<T> where T : IDisplayable
{
    ...
}
Thomas Levesque
Ah yes thank you that was my first idea, but I didn't tell you enough. The problem with this approach is that I have a lot of methods that take a MyList<T> and return a MyList<T> (for example methods like Select in Linq). So if I use select on an MyDisplayableList I get back a MyList and them I'm unable to display it...is there a type safe way to handle this problem in C#?
Jules
Then make that method generic: `public TList Foo<TList,TItem>(TList list) where TList : MyList<TItem>`
dtb
Thanks. I don't see how that would solve the problem however. For example if I did `aMyList.Select((a) => a.ToDisplayable())` then I'd have a `MyList<IDisplayable>` but I wouldn't be able to call GetControl() on it because it's not a MyDisplayableList.
Jules
`System.Linq.Enumerable.Select` returns an `IEnumerable<...>` not a `MyList<...>`
dtb
Yes but I'm writing a different select. The Linq syntax allows this.
Jules
+1  A: 

Do you even need generics here?

I'd guess you have multiple classes implementing IDisplayable and want to put all instances of them in the same list. So you'd just need a

public class MyList : Collection<IDisplayable>, IDisplayable
{
    public void GetControl()
    {
        foreach (IDisplayable displayable in this)
        {
            displayable.GetControl();
        }
    }
}

If you really want to put non-IDisplayable instances in that list as well, find the common base class and define a

public class MyList2 : Collection<object>, IDisplayable
{
    public void GetControl()
    {
        foreach (IDisplayable displayable in this.OfType<IDisplayable>())
        {
            displayable.GetControl();
        }
    }
}
dtb
+1  A: 

I think that the reason you want MyList<T> to be able to work with both IDisplayable and non-IDisplayable is because there's some duplicated function.

I would suggest that you have the base implementation as MyListBase<T> which implements the base funtctions that both the list performs. Then you have MyDisplayableList inherits MyList (MyDisplayableList<T> : MyList<T> where T : IDisplayable), which performs functions that is specific to IDisplayable only.

If there is any function that is specific to non-IDisplayble, add NonDisplayableList<T> : MyListBase<T> to perform these functions.

tvbusy
+1  A: 

I think a better solution to this problem is to abstract away from a list and think of a composite of IDisplayables. This is exactly how controls are modeled in ASP.NET or Windows Forms, for example.

public class CompositeDisplayable : IDisplayable {

  public void Add(IDisplayable displayable) {
    // TODO: check for null, cycles, etc...
    _list.Add(displayable);
  }

  public void Remove(IDisplayable displayable) {
    // TODO: check for null
    _list.Remove(displayable);
  }

  public Control GetControl() {
    var control = new Control(); 
    // this assumes Control is also a composite type
    _list.ForEach(displayable => control.Add(displayable.GetControl()));
    return control;
  }

  private List<IDisplayable> _list = new List<IDisplayable>();

}

Then, to bridge between your collection of "something" to the composite, you can create an extension method like this:

public static class DisplayableExtensions {

  public static IDisplayable ToDisplayable(this IEnumerable source) {
    if (source == null) throw new NullReferenceException(); // extension method should behave like instance methods
    var result = new CompositeDisplayable();
    source.
      OfType<IDisplayable>().
      ToList().
      ForEach(displayable => result.Add(displayable));
    return result;
  }

}
Jordão