C# Generics Question

OneOfTheseDays

Diamond Member
Jan 15, 2000
7,052
0
0
So here is what I'm essentially trying to do, and I have a distinct feeling I'm thinking about this problem in the wrong way since I can't figure out how to do this in C#.

I have a generic data buffer class, which I'm using to encapsulate the various data types that we support in our program. I want to include a reference to this class in a higher level class which is NOT generic. Right now my constraints are as follows for the generic class:

public class DataBuffer<T> where T : struct

So that only value types are allowed. I'm trying to use this class in a higher level concrete class as follows.

public class Signal
{
private DataBuffer<System.ValueType> m_DataBuffer;
}

The compiler barfs on this. I really have the feeling that I'm thinking about this problem in the wrong way and could use some guidance.
 

Markbnj

Elite Member <br>Moderator Emeritus
Moderator
Sep 16, 2005
15,682
14
81
www.markbetz.net
I think the issue is that a struct is a value type, but a value type is not always a struct. If you change your generic definition to

public class DataBuffer<T> where T : System.ValueType

does that work?

Edit: hmm, nope, the meaning of where T : struct is broader than I thought. Still, try what I suggested. It still seems like a simple base vs. derived class typing issue to me.
 
Last edited:

BigDH01

Golden Member
Jul 8, 2005
1,631
88
91
So here is what I'm essentially trying to do, and I have a distinct feeling I'm thinking about this problem in the wrong way since I can't figure out how to do this in C#.

I have a generic data buffer class, which I'm using to encapsulate the various data types that we support in our program. I want to include a reference to this class in a higher level class which is NOT generic. Right now my constraints are as follows for the generic class:

public class DataBuffer<T> where T : struct

So that only value types are allowed. I'm trying to use this class in a higher level concrete class as follows.

public class Signal
{
private DataBuffer<System.ValueType> m_DataBuffer;
}

The compiler barfs on this. I really have the feeling that I'm thinking about this problem in the wrong way and could use some guidance.

I don't believe C# allows you to specify ValueType as a constraint. If you remove that though, it should work.

Code:
class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Press any key to run the program...");
            Console.ReadKey();
            var @struct = new Post
            {
                PostId = 1,
                PostText = "basic test"
            };
            var buffer = new TypedBuffer<Post>();
            buffer.Add(@struct);
            var struct2 = buffer[0];
            Console.WriteLine(string.Format("PostId:{0}\tPostText:\"{1}\"", struct2.PostId, struct2.PostText));
            Console.WriteLine("Begin next test...");
            Console.ReadKey();
            var thread = new AnandtechThread();
            thread.Add(new Post
            {
                PostId = 2,
                PostText = "class test"
            });
            var struct3 = thread[0];
            Console.WriteLine(struct3.GetType());
            Console.ReadKey();
        }

        public class AnandtechThread
        {
            private TypedBuffer<System.ValueType> _Posts;

            public AnandtechThread()
            {
                _Posts = new TypedBuffer<ValueType>();
            }

            public void Add(ValueType item)
            {
                _Posts.Add(item);
            }

            public ValueType GetObjectById(int id)
            {
                return _Posts.Items.FirstOrDefault();
            }

            public ValueType this[int i]
            {
                get
                {
                    return _Posts.Items.ToList()[i];
                }
            }
        }

        public class TypedBuffer<T>
        {
            private List<T> _list;

            public TypedBuffer()
            {
                _list = new List<T>();
            }

            public void Add(T entity)
            {
                _list.Add(entity);
            }

            public T this[int i]
            {
                get
                {
                    return _list[i];
                }
            }

            public IQueryable<T> Items
            {
                get
                {
                    return _list.AsQueryable();
                }
            }
            
        }

        public struct Post
        {
            public int PostId;
            public string PostText;
        }
        
    }

The above works as expected and the second output on the console tells me it is a struct of post.

I should note, if you make the constraint T : class, T can be System.ValueType. However, then your structs won't work (I assume that's what you're trying to use). There is not a very elegant solution to this.

Edit again ;), structs in C# can implement interfaces.... might make this problem much easier.
 
Last edited:

RocksteadyDotNet

Diamond Member
Jul 29, 2008
3,152
1
0
You cant do what you're trying to do. You can do it thourgh reflection like the guys above said, but I think you're doing it wrong.

I don't think you know what you're trying to achieve. I think instead of using a generic class you should be using a base class for your data types. Then the databuffer doesnt need to be generic, it is just a databuffer of your interface.


public class Signal
{
private DataBuffer<System.ValueType> m_DataBuffer;
}

What is the point of this? You cant really do anything with m_DataBuffer because you dont know what the type is. If you just want to add and remote stuff from it just make it take and Object, rather than T.

They way you're describing it you dont know what T is at compile time, so what's the point of having a Generic class?


Edit: Guy above beat me to it. Said what I was saying. You dont need a generic class, just have the parameters be ValueType.
 
Last edited:

BigDH01

Golden Member
Jul 8, 2005
1,631
88
91
I think the issue is that a struct is a value type, but a value type is not always a struct. If you change your generic definition to

public class DataBuffer<T> where T : System.ValueType

does that work?

Edit: hmm, nope, the meaning of where T : struct is broader than I thought. Still, try what I suggested. It still seems like a simple base vs. derived class typing issue to me.

Given that he wanted to use ValueType, I'm assuming he's using structs and thus no inheritance. Interfaces probably the most elegant solution here.
 

Markbnj

Elite Member <br>Moderator Emeritus
Moderator
Sep 16, 2005
15,682
14
81
www.markbetz.net
Given that he wanted to use ValueType, I'm assuming he's using structs and thus no inheritance. Interfaces probably the most elegant solution here.

Right, but internally struct is derived from System.ValueType, so at the very least using System.ValueType to instantiate the generic, even if allowed, would not be specific enough to satisfy the constraint. As was pointed out there's a rule in front of it, but I get the feeling that's what's going on behind the scenes.
 

OneOfTheseDays

Diamond Member
Jan 15, 2000
7,052
0
0
Thank you all for the feedback.

I ended up keeping my DataBuffer<T> class, which I'm using to encapsulate arrays of type int, float, and double. In the higher level Signal class that makes use of it, I include a reference to each of the 3 possible types. I have an enum which allows users to select which data buffer is currently active and based on that enum value the Signal class uses the corresponding DataBuffer<T> object.

I'm fairly happy with this solution as it lets me swap the underlying data buffer that is being used fairly easily, and I was able to remove a lot of duplicated code and move it to my generic DataBuffer<T> class.
 

LokutusofBorg

Golden Member
Mar 20, 2001
1,065
0
76
You should read up on the Strategy Pattern. Three collections, only one of which is used at a time via a switch or if/else statement, is pretty rough design.

A generic class *can* implement an interface. If you are never returning your generic type parameter from any of your generic class's methods, then consuming classes do not need to know what the runtime type argument is. Your Signal class only needs a single member typed as the interface.

e.g.
Code:
public class DataBuffer<T> : IDataBuffer
{
  ...
}

public class Signal 
{
  private IDataBuffer _dataBuffer;

  ...
}
 

OneOfTheseDays

Diamond Member
Jan 15, 2000
7,052
0
0
You should read up on the Strategy Pattern. Three collections, only one of which is used at a time via a switch or if/else statement, is pretty rough design.

A generic class *can* implement an interface. If you are never returning your generic type parameter from any of your generic class's methods, then consuming classes do not need to know what the runtime type argument is. Your Signal class only needs a single member typed as the interface.

e.g.
Code:
public class DataBuffer<T> : IDataBuffer
{
  ...
}
 
public class Signal 
{
  private IDataBuffer _dataBuffer;
 
  ...
}

Incredible. This is exactly what I was looking for. I had no idea a generic class could implement an interface.

At some level, however, the users of the Signal class need to know the underlying data type of the buffer that is encapsulated by it, so I will still need to keep the data type enum. However, all I need to do is the following now:

IDataBuffer
{
Array Buffer {get;}
}

DataBuffer<T> : IBuffer where T : struct
{
T[] m_Buffer;

public Array Buffer { get { return m_Buffer; }}
}

Signal
{
private IDataBuffer m_DataBuffer;
public IDataBuffer DataBuffer { get { return m_DataBuffer; }}
}

Main()
{
Signal mySignal = new Signal(1024, DataType.Double);

switch (mySignal.DataType)
{
case Double:
(double[])mySignal.DataBuffer.Buffer;
break;
case Float:
(float[])mySignal.DataBuffer.Buffer;
}
}