Click here to Skip to main content
Rate this: bad
good
Please Sign up or sign in to vote.
See more: C# .NET C#4.0 VB10.0 VB.NET .NET4 , +
So I'm having a small problem when working with numerics. I can't really imagine that I am the only one with this problem, yet I can't find any solutions. So here it goes.
 
I am making an Attribute[^] which takes a numeric in its constructor (ANY numeric). Now here is the problem, there are 11(!) different types of numerics... Byte[^], SByte, Int16[^], UInt16[^], Double[^], Decimal[^]... You get the point.
So am I doomed to create 11 overloaded constructors (actually 22 since my constructor already had an overload)?
And what do I do with the numeric once it has been passed to my constructor? Do I also create 11 private fields that can hold each numeric or should I box it in an Object[^]?
 
In any case I will need a huge Case[^]/switch[^] statement later on to check for type of the numeric...
Any solutions for this problem? For example, taking an Int64[^] and UInt64[^] could take care for all non-decimal numerics, but I am not sure if this is good/accepted practice.
 
Thanks.
Posted 27-May-12 3:55am
Edited 27-May-12 4:00am
v2
Comments
Sandeep Mewara at 27-May-12 10:46am
   
5!
Naerling at 27-May-12 11:09am
   
Thanks!
Mehdi Gholam at 27-May-12 10:50am
   
5'ed for the question.
Naerling at 27-May-12 11:09am
   
Thank you.
burndavid93 at 27-May-12 11:18am
   
my 5.
Naerling at 27-May-12 11:22am
   
Thanks :D
Shahin Khorshidnia at 27-May-12 13:18pm
   
in Switch/case what does it do after checking the condition? If it does various things then there is no way.
Naerling at 27-May-12 14:20pm
   
Basically, my situation is that I get an Object which is a numeric type. I check its type, cast it and compare it to the value I got in the constructor (whichever out of 11 constructors was called).
micky_bird86 at 28-May-12 21:21pm
   
Good one
Naerling at 29-May-12 2:27am
   
Thanks :)
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 1

Usually attributes are processed once (type interrogation etc.) so performance is not critical so boxing is a viable option which reduces your code size.
 
I am afraid you can not escape a big switch statement [or a nested IF] (if you can let me know! I have tried Smile | :) [DataTypes.cs in RaptorDB Doc store]).
  Permalink  
Comments
Naerling at 27-May-12 11:17am
   
Actually these Attributes are processed quite a lot! Let's say once every few seconds... Basically every time the BindingCompleted Event of a Binding is raised and the Property that triggered it had the Attribute.
Naerling at 28-May-12 10:49am
   
Hi Mehdi. Unfortunately your answer didn't quite help, but thanks for a good effort.
I have found a way to escape a big switch statement by using IComparable. In my specific case I do not need to add, subtract etc. so as long as I know they're numerics (and I do, since my constructors only allow for numerics) I can safely compare them :)
Mehdi Gholam at 28-May-12 10:51am
   
Cheers
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 3

As far as I can see there is no way to have one single constructor. Using a ValueType[^] is an idea, but not very user friendly (since ValueType[^] can be more than just numerics, in which case I would have to throw an Exception[^]). By storing the numerics into a Private field as an IComparable[^] (as all numeric types are IComparable[^]) I can then simply call IComparable.CompareTo[^] to compare the value with whatever other numeric I get. This saves me the huge case statement (I am not interested in type anymore, since I can compare anyway).
Of course this only works because I do not have to add, subtract etc. and only need to compare. Any other tips, tricks, solutions etc. are still welcome. Especially on how I could still add, subtract etc. using only a single type and not 11...
 
Thanks everyone for the help so far!
  Permalink  
v3
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 4

How about dynamic?
If you are willing to pay the run-time cost, and if you are willing to give up on compile-time type check, dynamic could help.
 
C# really lacks generics specialization and (especially if run-time performance matters) compile-time elaboration of generics.
 
In C++ you can build type traits based on templates that were elaborated at compile-time (e.g. a pioneering book on that was and still is Modern C++ Design[^]).
 
The concept is based on three aspects:
  1. functional programming style for template programming
  2. define default behavior in a templated item, add specialization for the types in question (e.g. for the several numeric types)
  3. C++ allows to have values as template parameters (e.g. not only types but also compile-time constants)
 
Well, C# does not get even close to this Frown | :-(
 
Top be honest, I really dislike this "workaround", but you have at least another option to evaluate for your problem at hand.
 
Cheers
Andi
 
PS: here goes some sample code:
[AttributeUsage(AttributeTargets.Class)]
public class MyAttrAttribute : Attribute
{
    public dynamic Arg { get; private set; } // dynamic attribute
    public MyAttrAttribute(object arg)
    {
        Arg = arg;
    }
    public static MyAttrAttribute GetAttribute(Type owner)
    {
        return (from a in owner.GetCustomAttributes(false)
                where a.GetType() == typeof(MyAttrAttribute)
                select a
                ).FirstOrDefault() as MyAttrAttribute;
    }
}
 
[MyAttr(123)]
public class MyClassWithIntAttr
{
    public MyClassWithIntAttr()
    {
        var attr = MyAttrAttribute.GetAttribute(
                     MethodBase.GetCurrentMethod().DeclaringType);
        Console.WriteLine("{0} = {1}", attr, attr.Arg + 42); // may throw exception
    }
}
[MyAttr(3.14159265358)]
public class MyClassWithDoubleAttr
{
    public MyClassWithDoubleAttr()
    {
        var attr = MyAttrAttribute.GetAttribute(
                     MethodBase.GetCurrentMethod().DeclaringType);
        Console.WriteLine("{0} = {1}", attr, attr.Arg + 42); // may throw exception
    }
}
 
class Program
{
    public static void Main()
    {
        MyClassWithIntAttr c1 = new MyClassWithIntAttr();
        MyClassWithDoubleAttr c2 = new MyClassWithDoubleAttr();
    }
 
Output:
 
Sample.MyAttrAttribute = 165
Sample.MyAttrAttribute = 45.14159265358
  Permalink  
Comments
Wonde Tadesse at 28-May-12 16:20pm
   
5+
Andreas Gieriet at 28-May-12 16:25pm
   
Thanks!
Andi
Naerling at 29-May-12 13:44pm
   
Thanks for a fine answer. However, I am wondering how I could get this result in VB.NET?
It seems this is a C# only solution, correct?
In any case you have my 5 for this interesting C# solution :)
Andreas Gieriet at 30-May-12 2:39am
   
No idea about VB.Net. Google tells me that VB.Net does not know that feature. C# only knows that since C# 4.0. See solution #5 for an alternative.
Cheers
Andi
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 5

As a followup to solution #4: if you don't have the dynamic feature at hand (VB.Net, .Net version before 4.0), you may employ casts (see the As<T>(...) method):
 
    [AttributeUsage(AttributeTargets.Class)]
    public class MyAttrAttribute : Attribute
    {
        private object _arg;
        public MyAttrAttribute(object arg)
        {
            _arg = arg;
        }
        // cast to a needed type
        public static T As<T>(Type owner)
        {
            var attr = (from a in owner.GetCustomAttributes(false)
                    where a.GetType() == typeof(MyAttrAttribute)
                    select a
                    ).FirstOrDefault() as MyAttrAttribute;
            return attr != null ? (T)attr._arg : default(T);
        }
 
    }
 
    [MyAttr(123)]
    public class MyClassWithIntAttr
    {
        public MyClassWithIntAttr()
        {
            int value = MyAttrAttribute.As<int>(this.GetType());
            Console.WriteLine("attr + 42 = {0}", value + 42);
        }
    }
    [MyAttr(3.14159265358)]
    public class MyClassWithDoubleAttr
    {
        public MyClassWithDoubleAttr()
        {
            double value = MyAttrAttribute.As<double>(this.GetType());
            Console.WriteLine("attr + 42 = {0}", value + 42);
        }
    }
    class Program
    {
        public static void Main()
        {
            MyClassWithIntAttr c1 = new MyClassWithIntAttr();
            MyClassWithDoubleAttr c2 = new MyClassWithDoubleAttr();
        }
    }
 
Output:
attr + 42 = 165
attr + 42 = 45.14159265358
 
Cheers
Andi
  Permalink  
Comments
Naerling at 30-May-12 11:38am
   
Hi Andi and thanks for another answer. Really very interesting. The problem here though is that in order to use As you have to know the type of the numeric, and that's exactly what I don't have :)
Of course I could always use an int64 (anything from byte to int64), decimal (decimal) or double (double or float), since any numeric goes in those three types. In that case this would work pretty well. Although you'd still have a case (case decimal, case double, float, case else).
Pretty sweet solution! And thanks again.
Andreas Gieriet at 30-May-12 13:14pm
   
Hi, Naerling,
 
you are welcome!
 
I'm not really a fan of casting and storing items as object. So, I'd like to provide a better solution to this, but I have no clear idea what exactly you want to achieve. If you have an attrribute and pass *any* kind of numeric to it, what function/operation do you want to do on these different values? E.g. if you want to add some other value to it, you can only do that on values that can be implicitly converted one to the other.
 
Can you please provide a sample (pseudo) code showing how you would like to access the attribute's numeric value? That would help to find a suitable solution.
 
Cheers
Andi
Naerling at 30-May-12 13:39pm
   
Actually, the only thing I need to do is compare one to the other. Since all numerics are IComparable I could store them as such (see my own answer to this question).
Basically what I wanted is to build something that takes a numeric as parameter. I wanted each numeric to be valid as input (let's say memory is really an issue and all we can use is int16). Now at run-time I am getting an object that holds a numeric of the same type. All I need to do now is cast the object to the correct numeric and check if one is greater than the other.
 
// excuse my c# skills, it's not my first language :)
public class Test
{
Int16 _int16Field;
Int32 _int32Field;
 
public Test(Int16 numeric){ _int16Field = numeric; }
public Test(Int32 numeric){ _int32Field = numeric; }
// etc...
 
bool IsObjectGreater(object numeric)
{
// cast numeric to the correct type.
if (typeof(numeric) == gettype(Int16)){ return (Int16)numeric > _int16Field; }
elseif (typeof(numeric) == gettype(Int32)) { return (Int32)numeric > _int32Field; }
// etc...
}
}
 
This is a lot of work and not a very elegant solution. It doesn't even work since all the fields are put on the stack anyway and just using an Int64 would have been more efficient memory wise.
One solution is to have only Int64, decimal and double since those numerics fit all other numerics.
What I have now is one field which stores the numeric (coming from one of the 11 overloaded constructors) as IComparable.
Now I only need to do the following:
 
bool IsObjectGreater(object numeric)
{
// from the top of my head I am not quite sure if < or > should be used.
return _comparableField.Compare(numeric) < 0;
}
 
Easier would have been if we just had one numeric type that changed its underlying type dynamically, depending on the size of the numeric. Although I'm not sure how that would go memory or performance wise...
 
numeric n = 254; // underlying type is now byte, since byte is sufficient.
n += 2; // underlying type is now int16, since byte does not suffice anymore, but int16 is the next best thing.
 
At least that's better than checking for 11 types max or at least three...
Andreas Gieriet at 30-May-12 18:04pm
   
There are several aspects in this whole thread that I'll try to address.
1) Your initial post talks about Attributes having overloaded numeric constructors.
2) Your motivation on the different sizes seem to be memory consumption.
 
Combining the two:
1) Attributes are stored on code, not on variables. I.e. no matter if you have one instance of a type or 1000000, an attribute exists only *once* on the definition of the class, method, etc. As a consequence, memory consumption is completely irrelevant.
 
2) I would say: if memory consumption is a *real* concern, then C#/VB.Net are not the right languages/environments. You have little control over the memory layout and size of objects - there is not even a sizeof operator of managed objects (you may use unsafe code blocks, though). See http://msdn.microsoft.com/en-us/library/eahchzkf(v=vs.100).aspx .
 
There is a way to "simulate" C/C++ unions (see http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/60150e7b-665a-49a2-8e2e-2097986142f3/), but still, the union uses up at least as much memory as the largest object in the union eats up.
 
Again, it's not worth the effort to spare memory on that scale in managed code.
 
Now that I have "discussed away" the memory consumption as reasons for (magically) optimizing numeric type sizes, the question remain, if there is further need to have a general "numeric" type?
 
I challenge that: computer arithmetics has its intrinsic limits (precision, accuracy, number ranges, etc.), and since these limits differ by built-in numeric type, you must do a prudent decision on which numeric type matches your needs. This decision can not be made by the compiler nor by the environment.
 
If looking from a bit further away, you might invent our own "numeric" class tree that optimizes somehow memory, but maybe to the cost of performance. I would never ever do that, though: too much hassle for "nothing" - it becomes very soon very complex if you want a behavior like built-in types: operators, conversions within the type tree, Math library support, conversion from/to built-in types, ... (you gonna die).
 
My advise:
1) analyze the problem domain and decide which numeric type solves *your* problem
2) use that type only
3) document your decision and the resulting limits
 
Cheers
Andi
Naerling at 30-May-12 18:28pm
   
Best advice I've seen so far I guess.
The memory was just a theoretical example. I'm not really concerned by the memory that takes up a byte or an Int64... Not on modern computers anyway. I was simply curious to the limitations and possibilities of the different numeric types when comparing them to each other. It doesn't seem easy. Your solution 'decide which numeric type solves your problem and use that type only' saves a lot of headache though.
Thanks for the time and effort in making your answers. They have helped me a lot and it's really appreciated! :)
Andreas Gieriet at 31-May-12 11:04am
   
You are welcome!
Andi
Rate this: bad
good
Please Sign up or sign in to vote.

Solution 2

Have you considered using ValueType[^] as argument type of your constructor? As it is a larger subset than numerics, the utility in the above msdn article could be also of use.
  Permalink  
Comments
Naerling at 27-May-12 16:24pm
   
Interesting idea. I might just do that. I won't accept this as a final answer (yet), but have a 5 for the good idea!
Zoltán Zörgő at 27-May-12 16:54pm
   
Just give it a try. Thanks for the vote.
Naerling at 28-May-12 10:45am
   
Accepted your answer as it gave me the idea for my own answer (using 11 overloads for a user friendly interface, but storing everything into an IComparable field). Thanks for the help! :)
Wonde Tadesse at 28-May-12 16:18pm
   
5+
Zoltán Zörgő at 28-May-12 17:49pm
   
Thank you

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



Advertise | Privacy | Mobile
Web04 | 2.8.140814.1 | Last Updated 30 May 2012
Copyright © CodeProject, 1999-2014
All Rights Reserved. Terms of Service
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100