Generics and collections

M

Martin Robins

I do not know if what I am trying to do is impossible, however since I cannot work out how to do it I thought I would post it here in the hope of some assistance.

Consider the following code ...

using System;

public class Parameter<T> {

private T value;

public Parameter() {
}

public Parameter(T value)
: this() {
this.value = value;
}

public T Value {
get { return this.value; }
set { this.value = value; }
}
}

public class BooleanParameter : Parameter<Boolean> {
}

public class ByteParameter : Parameter<Byte> {
}

public class WordParameter : Parameter<UInt16> {
}

As you can see, this is a simple example where I am creating generic types that will all expose a 'Value' property, but each of the 'Value' properties will have a different data type.

If I want to create collections for these types, again this is fairly basic ...

using System.Collections.ObjectModel;

public class BooleanParameterCollection : Collection<BooleanParameter> {
}

public class ByteParameterCollection : Collection<ByteParameter> {
}

public class WordParameterCollection : Collection<WordParameter> {
}

However, what I actually want is to create a collection of Parameter<?> objects; that is, a single typed collection that can contain BooleanParameter, ByteParameter or WordParameter objects intermingled with each other and then determine which of the objects I am working with at run time.

I have tried various combinations of interfaces and base classes, but all to no avail. Is this possible, if so, can anybody help me see the light?

Thanks in advance.

Martin.
 
M

Marc Gravell

Well, you can use object - otherwise you need to use a layer of
abstraction, such as a pair of interfaces allowing both typed (generic)
and untyped access:

using System.Collections.Generic;
using System;
interface IValue
{
Type ValueType { get; }
object Value { get; set; }
}
interface IValue<T> : IValue
{
new T Value { get; set; }
}
class Parameter<T> : IValue<T>
{
public Type ValueType
{
get { return typeof(T); }
}
private T value;
public T Value
{
get { return this.value; }
set { this.value = value; }
}
object IValue.Value
{
get { return Value; }
set { Value = (T)value; }
}
public Parameter() { }
public Parameter(T value) { Value = value; }
}


static class Program
{
static void Main()
{
List<IValue> parameters = new List<IValue>();
parameters.Add(new Parameter<int>(5));
parameters.Add(new Parameter<string>("abc"));
}
}
 
M

Martin Robins

Thank you for a very prompt answer.

This looks like it will do exactly what I want. I now need to go off and do
some further testing.

Again, thanks for your help.

Martin.
 
I

Ignacio Machin ( .NET/ C# MVP )

I do not know if what I am trying to do is impossible, however since I cannot work out how to do it I thought I would post it here in the hope of someassistance.

Consider the following code ...

    using System;

    public class Parameter<T> {

        private T value;

        public Parameter() {
        }

        public Parameter(T value)
            : this() {
            this.value = value;
        }

        public T Value {
            get { return this.value; }
            set { this.value = value; }
        }
    }

    public class BooleanParameter : Parameter<Boolean> {
    }

    public class ByteParameter : Parameter<Byte> {
    }

    public class WordParameter : Parameter<UInt16> {
    }

As you can see, this is a simple example where I am creating generic typesthat will all expose a 'Value' property, but each of the 'Value' propertieswill have a different data type.

If I want to create collections for these types, again this is fairly basic ...

    using System.Collections.ObjectModel;

    public class BooleanParameterCollection : Collection<BooleanParameter> {
    }

    public class ByteParameterCollection : Collection<ByteParameter> {
    }

    public class WordParameterCollection : Collection<WordParameter> {
    }

However, what I actually want is to create a collection of Parameter<?> objects; that is, a single typed collection that can contain BooleanParameter,ByteParameter or WordParameter objects intermingled with each other and then determine which of the objects I am working with at run time.

I have tried various combinations of interfaces and base classes, but all to no avail. Is this possible, if so, can anybody help me see the light?

Thanks in advance.

Martin.

You have two options, either you use Object as the type of the
collection and by doing so ignore any use of Generics in the first
place Or you make all your types implement a common interface, most
probably you can do this
 
T

tadmill

class Parameter<T> : IValue<T>
{

Could this be re-written as
class Parameter<T> : IValue

It compiles and runs this way, but that might be because the compiler
is filling a missing piece or 2.
 
M

Martin Robins

Marc,

Great answer, does most of what I want to do, but is there a more reliable
way of setting the value when the Parameter<> object has been cast to the
IValue interface?

For example, setting the value of a Parameter<byte> to 16 results in an
error if the initial statement does not include the cast [p.Value = 21
instead of p.Value = (byte)21;] - the internal cast [this.Value = (T)value]
fails with an invalid cast exception.

I have also tried using
[TypeDescriptor.GetConverter(this.ValueType).ConvertFrom(value)] but this
fails also!

Thanks again.I have been struggling with this for a while now and I am now
much closer.
 
M

Marc Gravell

but is there a more reliable way of setting the value when the Parameter said:
has been cast to the IValue interface?

Ultimately, no. TypeConverter is a reasonable fallback; another option
(especially for cases like int/byte) is Convert.ChangeType - see if
that helps.

Marc
 

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