Search code examples
c#nhibernategenerics

Passing a constant as a C# generic type parameter


I have an NHibernate IUserType which I'm trying to use to bind a couple of fixed size double[] fields (of different size) to single database column as BLOBs. I have the following code but somehow I need to pass it a constant integer so it knows how big the array should be. I don't think there's a way to instantiate this type in a custom way at runtime so the size really needs to be part of the type itself. I'd rather not have two copies of this ugly class if I can avoid it!

public class DoubleArrayUserType<Size> : NHibernate.UserTypes.IUserType
{
    private int _size = sizeof(Size);

    public object Assemble(object cached, object owner)
    {
        if (cached == null)
            return null;

        if (cached == DBNull.Value)
            return null;

        if (!(cached is byte[]))
            throw new ArgumentException();

        var arrayBytes = cached as byte[];
        var arrayStream = new BinaryReader(new MemoryStream(arrayBytes));

        var values = new double[_size];
        for (int i = 0; i < _size; ++i)
            values[i] = arrayStream.ReadDouble();

        return values;
    }

    public object Disassemble(object value)
    {
        if (value == null)
            return DBNull.Value;

        if (value == DBNull.Value)
            return DBNull.Value;

        if (!(value is double[]))
            throw new ArgumentException();

        var values = value as double[];
        var bytes = new List<byte>(sizeof(double) * _size);
        for (int i = 0; i < _size; ++i)
            bytes.AddRange(BitConverter.GetBytes(values[i]));
        return bytes.ToArray();
    }

    public NHibernate.SqlTypes.SqlType[] SqlTypes
    {
        get { return new NHibernate.SqlTypes.SqlType[] { NHibernate.SqlTypes.SqlTypeFactory.GetBinaryBlob(1) }; }
    }
}

Solution

  • Here's a controversial solution (expecting downvotes!), it doesn't yield a constant, though. I'm sure you will be able to solve your problem a different way, as well:

    public interface IHasSize{
      int Size { get; }
    }
    
    public class MySize : IHasSize { 
      public int Size { get { return 4; } }
    }
    
    public class RequiresASize<TSize> where TSize : IHasSize, new()
    {
      private int _size = new TSize().Size;
    }
    
    public class ProvidesASize : RequiresASize<MySize>{
      //_size in base class will be 4
    }
    

    So RequiresASize<TSize> is your big class in your question. I'm using inheritance via the ProvidesASize type at the end to demonstrate how it would work.