Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Collection of a type with generic parameters

Discussion in 'Scripting' started by cranky, Nov 26, 2014.

  1. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    Hey guys, I am trying to make a Variable collection which will be loaded/saved and also changeable through a debug console. I'm having a design issue. I am creating a type called Variable<T>, where T is the type of value stored, such as float or int. The constructor would be something like (string name, T defaultValue).

    This is all good so far. The problem is storing these Variable objects. I'd like the variables to be accessible with a string key (like a Hashtable), but I am unsure of how to make a Dictionary of a generic type :S.

    I'd like to avoid casting and such if possible, as I'd like this to have good performance. I'd imagine getting variables with a piece of code like this: variableCollection.GetValue<float>("Sensitivity") or something of the sort.

    I'm not asking for anyone to make it for me, just hoping to be pointed in the right direction. Thanks!
     
  2. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
  3. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    Right, I understand that. But my Variable type is Variable<T>. I can't make a Dictionary<string,Variable<T>> :\
     
  4. Kirk Clawson

    Kirk Clawson

    Joined:
    Nov 4, 2014
    Posts:
    65
    What you're actually asking for, though you may not know the terminology, is the construction of an Unbounded generic type. That's something that .net can't do (horrible enum hacks aside).

    Generics have very specific rules about what gets defined where, and all of them require concrete types when you create an instance. There's no way to do this without resorting to casting, or the painfully slow dark arts of reflection.

    Edited to add:
    Performance of this sort of thing shouldn't be too awfully bad, though it's not very safe:
    Code (CSharp):
    1. public class KeyValueStore<TKey>
    2.     {
    3.         private readonly Dictionary<TKey, object> _data = new Dictionary<TKey, object>();
    4.  
    5.         public Variable<TValue> GetValue<TValue>(TKey key)
    6.         {
    7.             return (Variable<TValue>) _data[key];
    8.         }
    9.     }
     
    Last edited: Nov 26, 2014
    Stoven likes this.
  5. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    Hmm I just feel there is a clean way to do this and it's just beyond my reach...

    I may be able to adapt your solution and add some type safety checks. In the meantime, if anyone has any good ideas, please share :).

    Thanks for the help, everyone!
     
  6. Kirk Clawson

    Kirk Clawson

    Joined:
    Nov 4, 2014
    Posts:
    65
    After sleeping on it, best I could come up with was if you limit what types your Variable<T> uses, you could probably pull this sort of thing off:
    Code (CSharp):
    1. public class KeyValueStore<TKey>
    2. {
    3.     private Dictionary<TKey, Variable<int>> _intDict = new Dictionary<TKey, Variable<int>>();
    4.     private Dictionary<TKey, Variable<float>> _floatDict = new Dictionary<TKey, Variable<float>>();
    5.  
    6.     public Variable<int> GetInt(TKey key)
    7.     {
    8.         return _intDict[key];
    9.     }
    10.  
    11.     public Variable<float> GetFloat(TKey key)
    12.     {
    13.         return _floatDict[key];
    14.     }
    15. }
     
  7. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    You're also setting yourself up for a huge performance nightmare. Here's what you could do:

    Code (csharp):
    1.  
    2. public interface IVariable
    3. {
    4.    public Type ObjectType { get; }  
    5. }
    6.  
    then:

    Code (csharp):
    1.  
    2. public class Variable<T> : IVariable
    3. {
    4.     public Type ObjectType { get { return typeof(T); } }
    5.     public string Key { get; set; }
    6.     public T Value
    7.  
    8.     public Variable(string key, T value)
    9.     {
    10.  
    11.      }
    12. }
    13.  
    Then in your Dictionary make it: Dictionary<string, IVariable>

    Now here's the problem... how are you going to get that value? You could create a GetValue method in your interface but return type would have to be "object", then you cast the return value to whatever type is stored in ObjectType. Easy enough but here's the kicker:

    #1 - Variable<T> is a bad thing... because T might be int, string, whatever. So now you're creating a reference type to hold a value type and you're creating an allocation every time you do this. Of course you can make Variable<T> a struct which would avoid that issue... but you're still going to run into Issue #2 below

    #2 - The only way you'll be able to get the type is to call GetType which returns an object, then cast it to whatever the type actually is (which could be int, string, whatever). In this case you're boxing / unboxing and you're allocating anyway and feeding the GC monster.

    So... what you're trying to do is probably a bad idea. Try to think of a better solution.
     
  8. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    Funny, I actually already almost completed it with this same design, although I used an abstract class instead of an interface.

    Hmm... maybe instead of GetVariable<T> returning a value of type T, it could return a Variable<T> (after confirming that the types match), and then whatever object called GetVariable<T> would just cache the Variable<T> and be able to access a current value any time. Kind of messy :\, not sure if I want that.

    Thanks for the advice, guys!
     
    Last edited: Nov 26, 2014