[C#] Can't get the following generics based function to work.

Started by
17 comments, last by Telastyn 14 years, 2 months ago
I'm looking to implement a function that can take in an int, byte, short, float, double, etc. and wrap that number around a particular range. The following doesn't work, as T can't be converted. What have I done wrong?

        public T Wrap(T value, T min, T max)
        {
            return ((value - min) % (max - min + 1)) + min;
        }
The gentleman who posted originally posted the code (Zipster) used the following for C++:

public T Wrap<T>(T value, T min, T max)
{
   return ((value - min) % (max - min + 1)) + min;
}
He stated that if the value was less than min then the function did not work as intended. Can anyone suggest an alternative method for wrapping a number, and help with my generics problem with my C# function please? Thank you.
Advertisement
C# does not have "duck typing" (which is how the template version works in C++). Basically, you can't do it (at least, I can't think of a way) and you'll just have to provide the overloads manually.
The problem is that C# generics constrain what you can do with them. In that example, you specify no constraint, so the compiler knows they are (at least) of type object. You can't add or modulo object so it complains.

Unfortunately, there's no 'numeric' constraint. Your best bet would be to constrain them to IComparable and use a different algorithm to constrain the value.
If you can wait until C#4.0 is out, you can do it in that with the dynamic object type:
http://en.wikipedia.org/wiki/C_Sharp_4.0#Dynamic_member_lookup
"Duck typing" has to be one of the most obscure (and therefore hilarious) names for a programming concept I've seen in...probably three days. I love programming. :)

Obvious work-around if you're implementing the types yourself: declare a Wrappable interface.

However, there's a far more awesome way:

        public static T Wrap<T>(T value, T min, T max)        {            Type[] TTParams = new Type[] { value.GetType(), value.GetType() };            Type[] TIntParams = new Type[] { value.GetType(), typeof(Int32) };            System.Reflection.MethodInfo plusT =                    value.GetType().GetMethod("op_Addition", TTParams);            System.Reflection.MethodInfo plusInt =                    value.GetType().GetMethod("op_Addition", TIntParams);            System.Reflection.MethodInfo minusT =                    value.GetType().GetMethod("op_Subtraction", TTParams);            System.Reflection.MethodInfo modT =                    value.GetType().GetMethod("op_Modulus", TTParams);            // (value - min)            T temp1 = (T)minusT.Invoke(null, new object[] { value, min });            // (max - min + 1)            T temp2 = (T)minusT.Invoke(null, new object[] { max, min });            temp2 = (T)plusInt.Invoke(null, new object[] { temp2, 1 });            // ((value - min) % (max - min + 1))            T temp3 = (T)modT.Invoke(null, new object[] { temp1, temp2 });            // return ((value - min) % (max - min + 1)) + min;            return (T)plusT.Invoke(null, new object[] { temp3, min }); ;        }


I'm completely unfazed by (albeit interested in, particular where corroboration is provided) anything anyone has to say about performance on this one. I'm not a C# programmer, this baby took effort for me to write. >_>

Oh, and you'll get C#'s brand of null reference exception if you feed this method a data type that doesn't implement - and % for itself, and + for itself and Int32.

Oh, and I'm now cheating on Java. I feel kinda guilty.
It seems like every day, a new example surfaces of why I hate C# generics.

If they had a different name I'd like them better, but they are anything but generic.
Quote:Original post by cache_hit
It seems like every day, a new example surfaces of why I hate C# generics.

If they had a different name I'd like them better, but they are anything but generic.


You should look at Java's generics then. C#'s are wonderful in comparison.

[edit:]
And reflection invoked math ops are dog slow. You'd be better off using dynamics. I used reflection for Tangent's interface to math ops, and its essentially what killed my motivation. They're literally about 10,000 times slower than the equivalent cil instruction.
C# generics aren't bad, they just aren't always what you need completely. I would love to have both C# generics and C++ templates for places (like this) where generics just doesn't cut it.

Anywho, the problem of "take an arbitrary type and perform these operations on it" is still possible, just not with generics alone. Enter the world of Dynamic Methods:

using System;using System.Collections.Generic;using System.Diagnostics;using System.Linq;using System.Linq.Expressions;using System.Text;using NUnit.Framework;namespace ConsoleApplication1{    [TestFixture]    public class Program    {        static void Main(string[] args)        {            const int it = 100000000;            int n, d;            Benchmark(it, out n, out d);            Console.WriteLine("Iterations: {0}", 1000000);            Console.WriteLine("Static: {0}", n);            Console.WriteLine("Dynamic: {0}", d);            Console.Read();        }        static void Benchmark(int iterations, out int nondynamic, out int dynamic)        {            var dwrap = CreateWrap<int>();            Stopwatch w = new Stopwatch();            w.Start();            for (int i = 0; i < iterations; i++)                Wrap(5, 0, 10);            w.Stop();            nondynamic = (int)w.ElapsedMilliseconds;            w.Reset();            w.Start();            for (int i = 0; i < iterations; i++)                dwrap(5, 0, 10);            w.Stop();            dynamic = (int)w.ElapsedMilliseconds;        }        [Test]        public void WrapIntTest()        {            var dynamicWrap = CreateWrap<int>();            Assert.AreEqual(Wrap(5, 1, 100), dynamicWrap(5, 1, 100));        }        [Test]        public void WrapFloatTest()        {            var dynamicWrap = CreateWrap<float>();            Assert.AreEqual(Wrap(5f, 1f, 100f), dynamicWrap(5, 1, 100));        }        static int Wrap(int value, int min, int max)        {            return ((value - min) % (max - min + 1)) + min;        }        static float Wrap(float value, float min, float max)        {            return ((value - min) % (max - min + 1)) + min;        }        static Func<T, T, T, T> CreateWrap<T>()        {            var pValue = Expression.Parameter(typeof(T), "value");            var pMin = Expression.Parameter(typeof(T), "min");            var pMax = Expression.Parameter(typeof(T), "max");            var valueMinusMin = Expression.Subtract(pValue, pMin);            var maxMinusMin = Expression.Subtract(pMax, pMin);            var maxMinusMinPlus = Expression.UnaryPlus(maxMinusMin);            var mod = Expression.Modulo(valueMinusMin, maxMinusMinPlus);            var finalAdd = Expression.Add(mod, pMin);            return Expression.Lambda<Func<T, T, T, T>>(finalAdd, new[] { pValue, pMin, pMax }).Compile();        }    }}


(Updated with benchmarks, since I figured the next thing to come out of someone's mouth will involve performance)

If you define something that is not supported (does not implement +, -, %, and ++) then you will get an InvalidOperationException when calling CreateWrap<>(). Otherwise, it'll produce pretty much the same code as:

        static T X(T value, T min, T max)        {            var a = value - min;            var b = max - min;            b++;            var c = a % b;            return c + min;        }


Performance-wise, its nearly the same. Dynamic methods are slightly slower than actual code, but its still pretty damn impressive that it is so close.
NetGore - Open source multiplayer RPG engine
You just get the overhead of the delegate invocation, which is what? 1.5-3 times worse in normal usage? Not too bad.

Doing this straight with 4.0 dynamics should be speedier than that even.
Quote:Original post by Telastyn
You just get the overhead of the delegate invocation, which is what? 1.5-3 times worse in normal usage? Not too bad.

Doing this straight with 4.0 dynamics should be speedier than that even.

Not really, the behavior of dynamics is to do a runtime lookup of the appropriate methods. It's essentially reflection in disguise. Don't expect dynamic to be some amazing performance band aid for such code, it's not.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

This topic is closed to new replies.

Advertisement