locked
How to determine if a generic parameter is null or default? RRS feed

  • Question

  • The following code snippet gives me the compiler error: "Operator '==' cannot be applied to operands of type 'T' and 'T'"


    void Test<T>(T item)
    {
       if(item == default(T)) // error here
       ...
    }

     


    What is the reason behind this compiler error? I just want to see if the item is null for reference types, or default for value types. How can I do this?
    Tuesday, November 22, 2005 11:01 PM

Answers

  • Yeah... both do boxing. I guess if you intend to use value types that may not be a cost you're willing to pay. There's another way though:



    private static readonly IEqualityComparer<T> comparer = EqualityComparer<T>.Default;
       
    public static bool IsDefault(T t) {
        return comparer.Equals(t, default(T));
    }

     


    This way, for valuetypes *that implement IEquatable<T>* you don't get the boxing cost. For other valuetypes, the normal object.Equals(object) will be used for the comparison, which involves boxing. You also may want to allow clients of your class to specify their own IEqualityComparer<T>, like e.g. the Dictionary class does (allowing boxing-free comparison of valuetypes not implementing IEquatable<T>).
    Monday, November 28, 2005 5:22 PM

All replies

  • You can compare the result of default to null, just not directly to a generic object. default will return zero if it is a value type, regardless of the actual value, and null if it is a reference type.

    Here is my understanding of how to use default:



    public Form1()

    {

    InitializeComponent();

    Test(1);

    Test(this);

    }

    private void Test<T>(T item)

    {

    T temp = default(T);

    if (temp == null)

    {

    MessageBox.Show("Reference Type");

    }

    else

    {

    MessageBox.Show("Value Type");

    }

    }



     
    Tuesday, November 22, 2005 11:37 PM
  • Mark, how would you code the Test method to determine if the default value was passed into it? I need to be able to determine whether the parameter passed to me is the default for that type.
    Tuesday, November 22, 2005 11:40 PM
  • I'm not sure that I am following you. T is generic type, so the incoming parameter 'item' can be of any type. How would you associate a default value with such a situation? You could use item.ToString() and then compare to your default value, but I am still not sure I am understanding what you are trying to accomplish, so that might not make sense to you.

    Are you looking to have methods with default parameters filled in? That does not involve the use of the default keyword. Overloading is one way to accomplish that. Such as the example given in the help files:

       public void Test() { Test(9); }
       public void Test(int i)  {}
    Wednesday, November 23, 2005 12:21 AM
  • What I want to do is determine whether the generic parameter passed to me is the default value for that type (i.e., null for reference types, 0 for value types). I want to know whether the generic parameter passed is the default for that parameter.

    My actual situation is far more complex that what I'm posting, so to simplify, what I need is this:


    void Foo<T>(T item)
    {
        // determine if item is null (for a reference type) or default (for a value type)

        ?
    }

     


    This appears to be a limitation in .NET (or at least, C#) generics, but I'm not sure yet.

    Am I clear on the problem I'm running into?
    Wednesday, November 23, 2005 12:31 AM
  • Ok, I see what you are talking about now. Why not just cast to string after you know it is not null? With the exception of structs, that should work.



    T temp = default(T);

    if (temp == null)

    {

    MessageBox.Show("Reference Type");

    }

    else

    {

    MessageBox.Show("Value Type");

    if (temp.ToString() == item.ToString())

    {

    MessageBox.Show("Equals Default");

    }

    else

    {

    MessageBox.Show("Does Not Equal Default");

    }

    }



     

    Wednesday, November 23, 2005 12:54 AM
  • I suspect the easiest way of doing this is:


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


    However, that will call Equals in the case where both parameters are non-null, which isn't ideal. If you don't mind that happening, that may be a reasonable solution. I'll see if I can work out any others though...

    Jon



    Monday, November 28, 2005 11:14 AM
  • You could also try this, which doesn't call t.Equals() for reference types:


    if (ReferenceEquals(t, default(T)) || Equals(t, default(T)));
     

    Monday, November 28, 2005 1:24 PM
  • Chip & Jon, do either of your solutions induce boxing on value types?
    Monday, November 28, 2005 4:27 PM
  • Yes, I believe both of them involve boxing, given the signatures of the methods involved.

    Jon
    Monday, November 28, 2005 4:56 PM
  • Yeah... both do boxing. I guess if you intend to use value types that may not be a cost you're willing to pay. There's another way though:



    private static readonly IEqualityComparer<T> comparer = EqualityComparer<T>.Default;
       
    public static bool IsDefault(T t) {
        return comparer.Equals(t, default(T));
    }

     


    This way, for valuetypes *that implement IEquatable<T>* you don't get the boxing cost. For other valuetypes, the normal object.Equals(object) will be used for the comparison, which involves boxing. You also may want to allow clients of your class to specify their own IEqualityComparer<T>, like e.g. the Dictionary class does (allowing boxing-free comparison of valuetypes not implementing IEquatable<T>).
    Monday, November 28, 2005 5:22 PM