Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collection of generic types [duplicate]

I have an object (form) which contains a collection (.Fields) which I want to contain instances of a generic class (FormField).

The FormField, simply, is defined as such:

public class FormField<T>
{
    private Form Form;
    public T Value { get; set; }
    public string Name { get; set; }

    public void Process()
    {
        // do something
    }

    public FormField(Form form, string name, T value)
    {
        this.Name = name;
        this.Value = value;
        this.Form = form;
    }
}

This allows me to have FormField, FormField etc. and that part works great. What I want is a collection of "Formfields" regardless of the type, but I am forced into defining a type (it seems) such as:

public class Form
{

    string Code { get; set; }
    string Title { get; set; }
    int Year { get; set; }
    Guid ClientID { get; set; }

    ICollection<FormField<int>> Fields { get; set; }
}

What, I think, I want is an interface that allows me to abstract the type information and thus type the collection as instances of (for exxample) IFormField not FormField<>

But I can't see how to define this without strongly typing the collection in the interface...

Any help (including any alternative solutions!) would be greatly appreciated!

Thanks, Ben

like image 215
Ben Avatar asked Sep 23 '10 09:09

Ben


People also ask

What are generic collections?

A generic collection is strongly typed (you can store one type of objects into it) so that we can eliminate runtime type mismatches, it improves the performance by avoiding boxing and unboxing.

Is array a generic collection?

Array is a historic type that goes back to the time that there were no generics.

Why generics are used with collections?

By using generics, programmers can implement generic algorithms that work on collections of different types, can be customized, and are type safe and easier to read.


3 Answers

Here's some code to complete Jon's answer:

public interface IFormField
{
    string Name { get; set; }
    object Value { get; set; }
}

public class FormField<T> : IFormField
{
    private Form Form;
    public T Value { get; set; }
    public string Name { get; set; }

    public void Process()
    {
        // do something
    }

    public FormField(Form form, string name, T value)
    {
        this.Name = name;
        this.Value = value;
        this.Form = form;
    }

    // Explicit implementation of IFormField.Value
    object IFormField.Value
    {
        get { return this.Value; }
        set { this.Value = (T)value; }
    }
}

And in your form:

ICollection<IFormField> Fields { get; set; }
like image 103
Thomas Levesque Avatar answered Oct 19 '22 18:10

Thomas Levesque


Create a non-generic interface or base class, which probably includes everything FormField does except the type-specific bits. Then you can have an ICollection<IFormField>. Obviously you won't be able to use this in a strongly-typed way, in terms of the type of field being used - but you can use all the non-type-specific bits of it (e.g. the name and the form).

like image 24
Jon Skeet Avatar answered Oct 19 '22 20:10

Jon Skeet


Another option (an alternative to Jon's answer) is to apply the adapter pattern, which can be useful when:

  • you are unable to modify the type, and can thus not define a base-type for it.
  • or, there is a need to expose 'type-specific bits' (as Jon put it).

When you want to expose type-specific bits, you effectively have to create a non-generic wrapper. A short example:

class NonGenericWrapper<T> : IAdaptor
{
    private readonly Adaptee<T> _adaptee;

    public NonGenericWrapper(Adaptee<T> adaptee)
    {
        _adaptee = adaptee;
    }

    public object Value
    {
        get { return _adaptee.Value; }
        set { _adaptee.Value = (T) value; }
    }
}

Implementing this non-generic behavior in a base-type would effectively break the Liskov substitution principle, which is why I prefer the wrapper approach as I also argue in my blog post.

like image 1
Steven Jeuris Avatar answered Oct 19 '22 20:10

Steven Jeuris