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
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.
Array is a historic type that goes back to the time that there were no generics.
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.
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; }
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).
Another option (an alternative to Jon's answer) is to apply the adapter pattern, which can be useful when:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With