Home > OS >  What is/are the best way to handle primitive type polymorphism in c#?
What is/are the best way to handle primitive type polymorphism in c#?

Time:09-17

What is/are the best way to handle primitive type polymorphism in c# ? I'm stumbling on a lot of case where this is a question I have issues to answer in c#. I'm more proficient in java where it's never been an issue. C# offer a lot of possibilities but none seems right in a lot of use case.

I've tried dynamic, but stumbled on some case where the behavior was not the same as expected and it feels a bit dangerous if not completely understood. So I started considering it as a last resort option if none was satisfying.

In a lot of case, the best option i found is to test for each type and perform the cast manually... at least it is the safest option. But this seem to be a bad option in most case. It would not annoy me so much with a low level language but it feels wrong in c#

One examples that annoys me the most is when calling a method that accept multiple types. Here is an example with BitConverter.GetBytes(...) .

        /// <summary>
        /// assign an object to Int16 registers
        /// object must be of type handled by BitConverter
        /// BitConverter.GetBytes() is used
        /// </summary>
        /// <param name="value">value to convert</param>
        /// <returns>the value in Int[] registers format</returns>
        /// <exception cref="NotSupportedException">type is not supported</exception>
        public static Int16[] FromValue(object value)
        {

            try
            {
                byte[] bytes = BitConverter.GetBytes(value);
                Int16[] registers = FromByteArray(bytes);
                return registers;
            }
            catch (Exception e)
            {
                throw new NotSupportedException("Provided type is not supported", e);
            }
            
        }

This code doesn't work because I need to call GetBytes with the right type. how would you do in this case ? Is there a good general rule regarding this kind of issue ? Is there a must read article about this kind of issue I'm having ?

Edit: Relating to the preferred answer, I think one of my issue is that Generics seems to be more important in c# than in Java and I've not used them a lot. Still wondering about use case were dynamic should be used instead though.

CodePudding user response:

Have you tried generics?

public static Int16[] FromValue<T>(T value)

Inside the method, you could check for type and cast it (depends on the input format) and call the specific method.

switch (Type.GetTypeCode(typeof(T)))
{
    case TypeCode.Int32:
       BitConverter.GetBytes((int)value)
       ...
       break;
}

CodePudding user response:

I would use compiled expressions (for simplification I only show how to convert to byte array you can implement rest)

EDIT: I've added overloaded method which takes object as parameter - it is using cache, too(but preffered way is to use generics)

public class GenericBitConverter
{
    public static byte[] GetBytes<T>(T value) { return Converter<T>.Convert(value);}
    public static byte[] GetBytes(object value)
    {
        if (value == null)
            return null;
        var type = value.GetType();
        return cache.GetOrAdd(type, t =>
        {
            var par = Expression.Parameter(typeof(object));
            return Expression.Lambda<Func<object, byte[]>>(Expression.Invoke(Expression.Field(null, typeof(Converter<>).MakeGenericType(type), "Convert"), Expression.Convert(par, type)), par).Compile();
        })(value);
    }

    private class Converter<T>
    {
        private static Func<T, byte[]> Create()
        {
            var par = Expression.Parameter(typeof(T));
            try
            {
                return Expression.Lambda<Func<T, byte[]>>(Expression.Call(typeof(BitConverter), "GetBytes", Type.EmptyTypes, par), par).Compile();
            }
            catch (InvalidOperationException)
            {
                return v => throw new NotSupportedException(string.Format("Provided type {0} is not supported", typeof(T).FullName));
            }
        }
        public static readonly Func<T, byte[]> Convert = Create();
    }
    
    private static ConcurrentDictionary<Type, Func<object, byte[]>> cache = new ConcurrentDictionary<System.Type, System.Func<object, byte[]>>();
}

You can check how it works here

This code should cache delegate for every type so compilation of the expression is executed only once per type (even for not supported type)

CodePudding user response:

If the goal is to convert primitive types to Int16-arrays. I would just suggest using Bitconverter & an extension method, i.e:

public static short[] ToShort(this byte[] bytes){
  return FromByteArray(bytes);
}

That way calling bitconverter directly is just marginally longer:

BitConverter.GetBytes(value).ToShort();    

And this way you keep all type the type safety.

For the more general question about working with numeric types you have the .Net 7 feature Generic Math. That is probably the closest thing to polymorphism for primitive types.

  • Related