Tech Off Thread

18 posts

Forum Read Only

This forum has been made read only by the site admins. No new threads or comments can be added.

T == null => Boxing = Problem

Back to Forum: Tech Off
  • User profile image
    TomasDeml

    Hi!
    I have this code:

    public void Add<T>(T item)
    {
        if (item == null)
            throw new ArgumentNullException();
        ...
    }
    


    but there is a problem - when you look at MSIL code:

    ...
     L_0001: box !0
    ...
    


    There is a boxing, because T can be value type, so it is boxed to be compared against null (I suppose Smiley).

    I tried this:
    if (item == default(T))
        throw...
    ...
    


    but this does not compile.

    Does anybody know how to avoid that boxing?
    Thanks.

  • User profile image
    jrg

    Yeah, null itself of course serves as the placeholder for a reference type pointing nowhere.  I'm not putting much thought into this but it seems logical for a null to signify the existence of a reference type operand on the other side of the comparison operator.  int x = null; wouldn't make much sense right?

  • User profile image
    jrg

    Try testing the type perhaps:
    item.GetType().IsValueType;

  • User profile image
    littleguru

    jrg wrote:
    Try testing the type perhaps:
    item.GetType().IsValueType;


    Yeah! You should first check if it is a value type and decide then what to do.

  • User profile image
    gman

    jrg wrote:
    Try testing the type perhaps:
    item.GetType().IsValueType;


    uhhhhhhhhh if he is trying to avoid boxing purely for efficiency, this is hardly going to be a helpful solution.

    The question is really, who CARES if it gets boxed? I mean surely you have something else to worry about? If you are having a performance problem with your app, THIS AINT IT!

  • User profile image
    Minh

    gman wrote:

    The question is really, who CARES if it gets boxed? I mean surely you have something else to worry about? If you are having a performance problem with your app, THIS AINT IT!
    Agreed w/ gman here. "Add" is probably not going to be your critical path. IL's "box" probably has no perf degradation if it's passed a ref-type. FIDO.

  • User profile image
    jrg

    I agree as well.  We'd have to hear from the author what the intentions are, or what the problem is. 

    It was indicated that boxing was seen as a problem.  The method appears to be purposely very generic, perhaps for a library.  If boxing based performance was a problem where Add involved a significant order of N (huge sorting? crypto?), I think it would be a better path to polymorph and optimize rather than generalize.

  • User profile image
    TomasDeml

    Thanks for your answers.

    First, I'd like to say that I have no performance issues. Second, I'm developing a dictionary collection, which can hold many types of objects. The reason why I want to get rid of the boxing is not performance - just when I use generic types rather than System.Object to hold various types, I don't NEED any boxing Smiley

    Usage:

    InvariantDictionary<string> id = new InvariantDictionary<string>();
    Version v = new Version(1, 0, 0, 1);
    DateTime d = DateTime.Now;
    int i = 10;
    
    id.Add<Version>("foo", v);
    id.Add<DateTime>("foo1", d);
    id.Add<int>("foo2", i);
    
    Version v1 = id.Get<Version>("foo");
    DateTime d1 = id.Get<DateTime>("foo1");
    int i1 = id.Get<int>("foo2");
    

  • User profile image
    Minh

    TomasDeml wrote:
    I use generic types rather than System.Object to hold various types, I don't NEED any boxing Smiley
    Ahh, gotcha. I would guess that it's boxing because you're comparing it to null. I wonder if "box" would still be there if you don't null check -- which is probably needed in your case. Well, not too helpful, I know.

  • User profile image
    TomasDeml

    This is the final solution:

    // item.GetType().IsValueType() throws NullReferenceException when item is null, so...
    if (!typeof(T).IsValueType)
        if (item == null)
            throw new ArgumentNullException();
    ...
    


    The code above works fine, no boxing.

  • User profile image
    TomasDeml

    Code...

    if (item == default(T))
        ...
    


    ...does not compile, but this does:

    
    if (item.Equals(default(T)))
    
        ...
    
    


    This code is useless for me, because I just don't allow null keys, not 0 ones...


  • User profile image
    Sven Groot

    In C++ you would solve this by having two template functions, one
    template<typename T> void Add(T item);

    and a
    template<typename T> void Add(T *item);

    But that can't be done in C#.

  • User profile image
    jrg

    Oh man, you said the word "can't"... and I can't resist the word Tongue Out

    Not the same way of course, no.  Templates are such a cool OOP tool and the STL serves good testament to that.  I think it's just a paradigm shift in C# regarding types as they're all derived from Object and value types from Object then ValueType.

    public void Add(ValueType item)
    {
    }

    This should force the developer consuming your method to cast.  If they can't cast to a value type then you'd be assured when you do receive an object you're receiving a value type.  It's still an object, just limited to a specific type of core object.

    I haven't tested this, and in reading, ValueType is stated as the base type for structs whether struct is a subset of ValueType or the defiinition of the set I don't know... 10 second test program time Smiley

  • User profile image
    Sven Groot

    But that's not the same thing. I'm talking about specializing a template not to work (or only work) for value types. What you presented isn't a template fuction anymore.

    EDIT: Actually, maybe you can do it using "where T: struct". I'm going to test that.

  • User profile image
    Sven Groot

    jrg wrote:
    Forcing a reference type in one method signature can be done with "ref":

    public void Add(ref Object referenceType)
    {
    }

    That is wrong. The parameter is passed by reference if ref is used, it is not a constraint. Value types can be passed by reference just as easily.

    And I've discovered where doesn't work because the C# compiler doesn't allow overloads that differ only by constraint.

    And although using two methods, one with an object parameter and one with a ValueType parameter will work, you're still not using generics, which was the entire point to begin with.

  • User profile image
    jrg

    I think in a way it's the same yet an inverse, if that makes any sense.  An analogy would be that Object is the universal template for all types.  The problem though is when an implicit cast happens, right?  If the compiler casts for you, it just did an end-around on your strategy to force a template.

    Here we go...
    Forcing a reference type in one method signature can be done with "ref":

    public void Add(ref Object referenceType)
    {
    }

    // this does not cover all other types
    public void Add(ValueType valueType)
    {
    }

    I just tested and when you have only those two method signatures, you have nothing covering a type such as a String.  So ValueType appears to be specific to structs.

  • User profile image
    jrg

    Yep... it's a pretty cool exercise though... you never know what you don't know until it presents itself and you work it out. Smiley

  • User profile image
    jrg

    Here's a fun one I ran into yesterday... not exactly part of this thread but I don't think it's justified to start a new thread:

    Given an abstract class, create a field that is readonly which can be set by inheriting classes.

    const fields are compiler time only.
    readonly fields are allowed to be set at runtime in the constructor or as a field initialization.  This definition is a little gray in addressing "what about inheriting types and their constructors?"

    The answer is that readonly cannot be set by the inheriting class.

    Here's one way to get that functionality though:

    public abstract class A
    {
       protected readonly int myField;
       protected A(int myField)
       {
          this.myField = myField;
       }
    }

    public class B:A
    {
       public B(): base(4) // valid
       {
       // base.myField = 4; // invalid
       }
    }

Conversation locked

This conversation has been locked by the site admins. No new comments can be made.