[.net] 4.0 - Less than impressive

Started by
6 comments, last by Narf the Mouse 14 years, 1 month ago
I've been trying out the new dynamic and contra-variance and covariance. Problems: You can't use the "where" keyword with dynamic variables. You can only use contra- and co-variance in delegates and interfaces. Rather limited. For some reason, after specifying contra-variance in a delegate, you can't use "where" to specify that the generic(s) are classes derived from a base class. No reference conversion. ...Isn't a constraint supposed to specify that an input has to be of a certain type, not that an input is to be converted to a certain type? Certainly a shallow look, but once again my search for an easy implementation of a generic Number class/struct/whatever is halted by code constraints. Also, C#'s difficulty with anything "out of bounds". Oh, and it appears you still can't specify operators in an interface.
Advertisement
Regarding where+dynamic, would it be possible to implement it by adding new extension methods taking dynamic parameters?

Here's some untested code (I don't have VS 2010 handy):
public static class DynamicEnumerable{  public static IEnumerable<dynamic> Where(this IEnumerable<dynamic> source, Func<dynamic, bool> selector)  {    foreach (var item in source)    {      if (selector(item))      {        yield return item;      }    }  }}

In the meantime, I've relented somewhat; I've been very stressed lately and that may have coloured my perceptions. But, in any case, while I'm not too impressed with what it can do now (It also seems some promised features aren't there; I seem to remember something about function bodies in interfaces), it does seem like it would be a good base to expand from.

As for your suggestion, I've got a 2010 express beta console test project; let's see what I can make of it.
Well, after a bunch of guesses (Since, really, no clue if extending "where" is even possible, much less how - I have nothing.

Hopefully it can work - And thanks for the idea. :)
    // struct Test<T> where T : IEnumerable<dynamic>    // struct Test<T> where T : dynamic    {    }    // Regarding where+dynamic, would it be possible to implement it by adding new extension methods taking dynamic parameters?    // Here's some untested code (I don't have VS 2010 handy):    public static class DynamicEnumerable    {        // public static IEnumerable<dynamic> Where(IEnumerable<dynamic> source, Func<dynamic, bool> selector)        // public static dynamic where(dynamic t, Func<dynamic, bool> selector)        // public static dynamic Where(dynamic t)        // public static T Where<T>(T t, Func<dynamic, bool> selector)        // public static T where<T>(T t)        // public static where Where(dynamic t)        // public static where<T> Where<T>(T t)        {            if (t.GetType() == typeof(String))                return t;            return null;            /*foreach (var item in source)            {                if (selector(item))                {                    yield return item;                }            } */        }    }

Edit: Oh, and the error I get is the "can't use dynamic as a keyword" one.
No, you likely can't use dynamic with where since where is specifically syntactic sugar for enumerables and dynamic isn't.

And no, there's not likely to be any realistic typed 'number' generic. Even between different sorts of numbers, there's no consistent pattern for operations and the resultant types. If you want an algorithm to work on arbitrary things, make it a free function that takes dynamics and just pass the right thing in. Given .NET constraints I don't think you'll ever get better.

And realistically you're not going to get operators on an interface for similar reasons. You run into ambiguity issues on the dispatch.


I was kinda down on 4.0 for a while, but dynamic is fine once you realize that it only really has 2 uses:

- getting rid of all the casting/serialization you'd need to do if you used object/string instead
- enabling dynamic dispatch on methods
It took some hacking, but your idea worked: A Scalar struct which can do any mathematical operation, irrelevant of the underlying type - And which doesn't do any of it's own type checking. (I've made Scalars before, but they all needed to do their own type checking)

Granted, I cheated and just used two underlying types (Int64 and Double), but if you don't care about types (And speed!) enough to use a Scalar, you probably don't care about underlying types, either.

Once I get around to finishing my BigNumber class (It just needs division), I'll add that as an option.

In any case, Scalar should be usable in any situation where you'd use a typed number for math or a function. Do be aware that the Scalar could be out of bounds for that function (Say, "20,513,213,125" for an Int32 function...), so beware of that.

Edit: Had to change the implicity conversion to explicit conversion, because it was messing up the math - It'd add them first as Scalars and then second as whatever the underlying type is and return that result.

Bizzare.

Source:
    public struct Scalar    {        #region Body        #region Creation        public Scalar(Int64 value)        {            this.value = value;        }        public Scalar(Double value)        {            this.value = value;        }        #endregion        private dynamic value;        #region Mathematics        #region Addition        public static Scalar operator +(Scalar a, Scalar b)        {            return Add(a.value, b.value);        }        public static Scalar Add(Int64 a, Int64 b)        {            Int64 temp = a + b;            Int64 temp2 = ((a / 2) + (b / 2));            if ((temp2 < 0 && temp > 0) ||                (temp2 > 0 && temp < 0) )                return new Scalar((Double)a + b);            else                return new Scalar(temp);        }        public static Scalar Add(Int64 a, Double b)        {            return new Scalar(a + b);        }        public static Scalar Add(Double a, Int64 b)        {            return new Scalar(a + b);        }        public static Scalar Add(Double a, Double b)        {            return new Scalar(a + b);        }        #endregion        #region Add 1        public static Scalar operator ++(Scalar a)        {            return Add(a.value);        }        public static Scalar Add(Int64 a)        {            Int64 temp = a + 1;            Int64 temp2 = ((a / 2) + 1);            if ((temp2 < 0 && temp > 0) ||                (temp2 > 0 && temp < 0))                return new Scalar((Double)a + 1);            else                return new Scalar(temp);        }        public static Scalar Add(Double a)        {            return new Scalar(a + 1);        }        #endregion        #region Posate        public static Scalar operator +(Scalar a)        {            return Posate(a.value);        }        public static Scalar Posate(Int64 a)        {            return new Scalar(Math.Abs(a));        }        public static Scalar Posate(Double a)        {            return new Scalar(Math.Abs(a));        }        #endregion        #region Subtraction        public static Scalar operator -(Scalar a, Scalar b)        {            return Subtract(a.value, b.value);        }        public static Scalar Subtract(Int64 a, Int64 b)        {            Int64 temp = a + b;            Int64 temp2 = ((a / 2) + (b / 2));            if ((temp2 < 0 && temp > 0) ||                (temp2 > 0 && temp < 0))                return new Scalar((Double)a - b);            else                return new Scalar(temp);        }        public static Scalar Subtract(Int64 a, Double b)        {            return new Scalar(a - b);        }        public static Scalar Subtract(Double a, Int64 b)        {            return new Scalar(a - b);        }        public static Scalar Subtract(Double a, Double b)        {            return new Scalar(a - b);        }        #endregion        #region Subtract 1        public static Scalar operator --(Scalar a)        {            return Subtract(a.value);        }        public static Scalar Subtract(Int64 a)        {            Int64 temp = a - 1;            Int64 temp2 = ((a / 2) - 1);            if ((temp2 < 0 && temp > 0) ||                (temp2 > 0 && temp < 0))                return new Scalar((Double)a - 1);            else                return new Scalar(temp);        }        public static Scalar Subtract(Double a)        {            return new Scalar(a - 1);        }        #endregion        #region Negate        public static Scalar operator -(Scalar a)        {            return Negate(a.value);        }        public static Scalar Negate(Int64 a)        {            return new Scalar(-a);        }        public static Scalar Negate(Double a)        {            return new Scalar(-a);        }        #endregion        #region Multiplication        public static Scalar operator *(Scalar a, Scalar b)        {            return Multiply(a.value, b.value);        }        public static Scalar Multiply(Int64 a, Int64 b)        {            Int64 temp = a * b;            if (temp < a || temp < b)                return new Scalar((Double)a * b);            else                return new Scalar(temp);        }        public static Scalar Multiply(Int64 a, Double b)        {            return new Scalar(a * b);        }        public static Scalar Multiply(Double a, Int64 b)        {            return new Scalar(a * b);        }        public static Scalar Multiply(Double a, Double b)        {            return new Scalar(a * b);        }        #endregion        #region Division        public static Scalar operator /(Scalar a, Scalar b)        {            return Divide(a.value, b.value);        }        public static Scalar Divide(Int64 a, Int64 b)        {            Int64 temp = a / b;            if (temp < a || temp < b)                return new Scalar((Double)a / b);            else                return new Scalar(temp);        }        public static Scalar Divide(Int64 a, Double b)        {            return new Scalar(a / b);        }        public static Scalar Divide(Double a, Int64 b)        {            return new Scalar(a / b);        }        public static Scalar Divide(Double a, Double b)        {            return new Scalar(a / b);        }        #endregion        #endregion        #region Comparison        #region Greater than        public static Boolean operator >(Scalar a, Scalar b)        {            return GreaterThan(a.value, b.value);        }        public static Boolean GreaterThan(Int64 a, Int64 b)        {            return a > b;        }        public static Boolean GreaterThan(Int64 a, Double b)        {            return a > b;        }        public static Boolean GreaterThan(Double a, Int64 b)        {            return a > b;        }        public static Boolean GreaterThan(Double a, Double b)        {            return a > b;        }        #endregion        #region Greater than or equals        public static Boolean operator >=(Scalar a, Scalar b)        {            return GreaterThanOrEquals(a.value, b.value);        }        public static Boolean GreaterThanOrEquals(Int64 a, Int64 b)        {            return a >= b;        }        public static Boolean GreaterThanOrEquals(Int64 a, Double b)        {            return a >= b;        }        public static Boolean GreaterThanOrEquals(Double a, Int64 b)        {            return a >= b;        }        public static Boolean GreaterThanOrEquals(Double a, Double b)        {            return a >= b;        }        #endregion        #region Less than        public static Boolean operator <(Scalar a, Scalar b)        {            return LessThan(a.value, b.value);        }        public static Boolean LessThan(Int64 a, Int64 b)        {            return a < b;        }        public static Boolean LessThan(Int64 a, Double b)        {            return a < b;        }        public static Boolean LessThan(Double a, Int64 b)        {            return a < b;        }        public static Boolean LessThan(Double a, Double b)        {            return a < b;        }        #endregion        #region Less than or equals        public static Boolean operator <=(Scalar a, Scalar b)        {            return LessThanOrEquals(a.value, b.value);        }        public static Boolean LessThanOrEquals(Int64 a, Int64 b)        {            return a <= b;        }        public static Boolean LessThanOrEquals(Int64 a, Double b)        {            return a <= b;        }        public static Boolean LessThanOrEquals(Double a, Int64 b)        {            return a <= b;        }        public static Boolean LessThanOrEquals(Double a, Double b)        {            return a <= b;        }        #endregion        #region Equals        public static Boolean operator ==(Scalar a, Scalar b)        {            return Equals(a.value, b.value);        }        public static Boolean Equals(Int64 a, Int64 b)        {            return a == b;        }        public static Boolean Equals(Int64 a, Double b)        {            return a == b;        }        public static Boolean Equals(Double a, Int64 b)        {            return a == b;        }        public static Boolean Equals(Double a, Double b)        {            return a == b;        }        #endregion        #region NotEquals        public static Boolean operator !=(Scalar a, Scalar b)        {            return NotEquals(a.value, b.value);        }        public static Boolean NotEquals(Int64 a, Int64 b)        {            return a != b;        }        public static Boolean NotEquals(Int64 a, Double b)        {            return a != b;        }        public static Boolean NotEquals(Double a, Int64 b)        {            return a != b;        }        public static Boolean NotEquals(Double a, Double b)        {            return a != b;        }        #endregion        #endregion        #region Conversion        #region Conversion to        public static implicit operator Scalar(Int16 value) { return new Scalar((Int64)value); }        public static implicit operator Scalar(Int32 value) { return new Scalar((Int64)value); }        public static implicit operator Scalar(Int64 value) { return new Scalar((Int64)value); }        public static implicit operator Scalar(UInt16 value) { return new Scalar((Int64)value); }        public static implicit operator Scalar(UInt32 value) { return new Scalar((Int64)value); }        public static implicit operator Scalar(UInt64 value) { return new Scalar((Double)value); }        public static implicit operator Scalar(Single value) { return new Scalar((Double)value); }        public static implicit operator Scalar(Double value) { return new Scalar((Double)value); }        #endregion        #region Conversion from        public static implicit operator Int16(Scalar a) { return (Int16)a.value; }        public static implicit operator Int32(Scalar a) { return (Int32)a.value; }        public static implicit operator Int64(Scalar a) { return (Int64)a.value; }        public static implicit operator UInt16(Scalar a) { return (UInt16)a.value; }        public static implicit operator UInt32(Scalar a) { return (UInt32)a.value; }        public static implicit operator UInt64(Scalar a) { return (UInt64)a.value; }        public static implicit operator Single(Scalar a) { return (Single)a.value; }        public static implicit operator Double(Scalar a) { return (Double)a.value; }        #endregion        #endregion        #region Over-rides        public override bool Equals(object obj)        {            return value.Equals(obj);        }        public override int GetHashCode()        {            return value.GetHashCode();        }        public override string ToString()        {            return value.ToString();        }        #endregion        #endregion    }
I think you completely missed the point of dynamic objects. You can simply write the following, and it will work as expected. You don't need a gigantic wrapper class with every permutation of mathematical operation defined as a separate method. That's an overhead and maintenance nightmare.
dynamic a = 5;dynamic b = 2.0;Console.WriteLine(a + b);


Additionally, .NET 4.0 already has a BigNumber class, so there's no reason to write your own (most likely worse) version of it.

If you want a way to interact with numeric types in a generic manner in earlier versions of .NET, you can make use of expression trees to compile operators as delegates that are roughly the same speed as a normal method call would be.
Mike Popoloski | Journal | SlimDX
Quote:Original post by Mike.Popoloski
I think you completely missed the point of dynamic objects. You can simply write the following, and it will work as expected. You don't need a gigantic wrapper class with every permutation of mathematical operation defined as a separate method. That's an overhead and maintenance nightmare.
*** Source Snippet Removed ***

Additionally, .NET 4.0 already has a BigNumber class, so there's no reason to write your own (most likely worse) version of it.

If you want a way to interact with numeric types in a generic manner in earlier versions of .NET, you can make use of expression trees to compile operators as delegates that are roughly the same speed as a normal method call would be.

1) Learning does not happen by blinding using what others have made.
2) I have yet to learn Linq. It's on the list, but so is a lot of other stuff.
3) What it has is a BigInteger class. What I have is a BigNumber class. The difference? Mine does decimals.
4) The "Add" functions don't exist just so I can complex things. Int64.MaxValue + Int64.MaxValue wraps, by default. Scalar converts it to Double, resulting in a correct (Although approximate, due to the nature of Double) result.
5) I did, in fact, think this through. As you can see.

This topic is closed to new replies.

Advertisement