196

This is probably not possible, but I have this class:

public class Metadata<DataType> where DataType : struct
{
    private DataType mDataType;
}

There's more to it, but let's keep it simple. The generic type (DataType) is limited to value types by the where statement. What I want to do is have a list of these Metadata objects of varying types (DataType). Such as:

List<Metadata> metadataObjects;
metadataObjects.Add(new Metadata<int>());
metadataObjects.Add(new Metadata<bool>());
metadataObjects.Add(new Metadata<double>());

Is this even possible?

5
  • 30
    I wonder if there's any real benefit to the approaches in the below answers compared to just using a List<object>? They won't stop boxing/unboxing, they won't remove the need for casting, and ultimately, you are getting a Metadata object that does not tell you anything about the actual DataType, I was searching for a solution to address those issues. If you're going to declare an interface/class, just for the sake of being able to put the implementing/derived generic type in a generic list, just how different is that than using a List<object> other than having a meaningless layer? Commented Nov 17, 2012 at 18:11
  • 10
    Both the abstract base class and interface provide a degree of control by restricting the type of elements that can be added to the list. I also can't see how boxing comes into this. Commented Feb 27, 2014 at 20:06
  • 3
    Of course, if you are using .NET v4.0 or higher then covariance is the solution. List<Metadata<object>> does the trick. Commented Feb 27, 2014 at 20:50
  • 2
    @0b101010 I was thinking the same, but unfortunately variance is not allowed on value types. Since OP has a struct constraint, it doesn't work here. See Commented Jul 7, 2014 at 7:33
  • 1
    @0b101010, Both only restrict reference types, any built-in value type and any struct can still be added. Also, in the end, you have a list of MetaData reference types instead of your original value types with no (compile time) information about the underlying value type of each element, that's effectively "boxing". Commented Sep 29, 2014 at 20:54

3 Answers 3

239
public abstract class Metadata
{
}

// extend abstract Metadata class
public class Metadata<DataType> : Metadata where DataType : struct
{
    private DataType mDataType;
}
Sign up to request clarification or add additional context in comments.

10 Comments

I have a similar problem, but my generic class extends from another generic class, so I can't use your solution... any ideas on a fix for this situation?
Is there a benefit to this approach compared to a simple List<object>? please look at my comment posted under OP's question.
@leppie: Is this possible to add multiple instances of Metadata to the list too?
@SaebAmini A List<object> doesn't show any intent to a developer, nor does it prevent a developer from shooting themselves in the foot by erroneously adding some non-MetaData object to the list. By using a List<MetaData> it is understood what the list should contain. Most likely MetaData will have some public properties/methods that haven't been shown in the above examples. Accessing those through object would require a cumbersome cast.
- Reply a correct answer to a question - Refuses to elaborate further - Leaves * Insert giga chad meme *
|
101

Following leppie's answer, why not make MetaData an interface:

public interface IMetaData { }

public class Metadata<DataType> : IMetaData where DataType : struct
{
    private DataType mDataType;
}

4 Comments

Can someone tell me why this approach is better?
Because no common functionality is shared - why waste a base class on that then? An interface is sufficient
Because you can implement interfaces in struct.
Class inheritance using virtual methods, however, is roughly 1.4x faster than interface methods. So if you plan on implementing any non-generic MetaData (virtual) methods/properties in MetaData<DataType>, choose an abstract class rather than an interface, if performance is a concern. Otherwise, using an interface can be more flexible.
41

I have also used a non-generic version, using the new keyword:

public interface IMetadata
{
    Type DataType { get; }

    object Data { get; }
}

public interface IMetadata<TData> : IMetadata
{
    new TData Data { get; }
}

Explicit interface implementation is used to allow both Data members:

public class Metadata<TData> : IMetadata<TData>
{
    public Metadata(TData data)
    {
       Data = data;
    }

    public Type DataType
    {
        get { return typeof(TData); }
    }

    object IMetadata.Data
    {
        get { return Data; }
    }

    public TData Data { get; private set; }
}

You could derive a version targeting value types:

public interface IValueTypeMetadata : IMetadata
{

}

public interface IValueTypeMetadata<TData> : IMetadata<TData>, IValueTypeMetadata where TData : struct
{

}

public class ValueTypeMetadata<TData> : Metadata<TData>, IValueTypeMetadata<TData> where TData : struct
{
    public ValueTypeMetadata(TData data) : base(data)
    {}
}

This can be extended to any kind of generic constraints.

3 Comments

+1 just because you are showing how to use it (DataType and object Data helped a lot)
I don't seem to be able to write for example Deserialize<metadata.DataType>(metadata.Data);. It tells me cannot resolve symbol metadata. How to retrieve the DataType to use it for a generic method?
@Cœur A little late to the party, but your question is answered in How do I use reflection to call a generic method?. (Comment added for myself and others who get here by search).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.