11

I'm converting a bunch of code from VB to C# and I'm running in to an issue with a method. This VB method works great:

Public Function FindItem(ByVal p_propertyName As String, ByVal p_value As Object) As T

    Dim index As Int32

    index = FindIndex(p_propertyName, p_value)

    If index >= 0 Then
        Return Me(index)
    End If

    Return Nothing

End Function

It allow the return of Nothing(null) for T.

The C# equivalent does not work:

public T FindItem(string p_propertyName, object p_value)
{
  Int32 index = FindIndex(p_propertyName, p_value);

  if (index >= 0) {
    return this[index];
  }
  return null;
}

It won't compile with this error:

The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable<T>'

I need to be able to have the same functionality or it will break a lot of code. What am I missing?

cuongle
  • 74,024
  • 28
  • 151
  • 206
Robert Beaubien
  • 3,126
  • 4
  • 22
  • 29

3 Answers3

48

Since T can be either reference type or value type, so returning null will not satisfy if T is value type. You should return:

return default(T);

From the link default keyword:

Given a variable t of a parameterized type T, the statement t = null is only valid if T is a reference type and t = 0 will only work for numeric value types but not for structs. The solution is to use the default keyword, which will return null for reference types and zero for numeric value types. For structs, it will return each member of the struct initialized to zero or null depending on whether they are value or reference types. For nullable value types, default returns a System.Nullable, which is initialized like any struct.

Update (2021-09-27)

With the new syntax from C# 7.1, you can be able to return:

return default;
cuongle
  • 74,024
  • 28
  • 151
  • 206
  • default(T) returns a new instance of T, not a null. There is about 30 million lines of code that use this assembly. I can't change how it works. It has to return a nothing/null value. Is there a way to set the default value to null? – Robert Beaubien Jan 25 '15 at 04:35
  • 4
    @RobertBeaubien: `default(T)` will return `null` if T is reference type, please check the link. – cuongle Jan 25 '15 at 04:42
  • 2
    @RobertBeaubien: if you really want to return `null`, you need to constraint T is reference Type by using `where T: class` but you should notice this way will not work if T is value Type – cuongle Jan 25 '15 at 04:48
  • 2
    @RobertBeaubien VB.Net's `Nothing` is equivalent to C#'s `default()`, not `null`. https://msdn.microsoft.com/en-us/library/0x9tb07z.aspx – Preston Guillot Jan 25 '15 at 06:23
  • Not sure what you mean by Reference type. Here is the class definition and T is always inherited from BaseObjects.BaseClass : public class BaseGenericCollection : BindingList – Robert Beaubien Jan 25 '15 at 07:13
  • If you are sure that T is always reference type (class), you can constraint T in your method: `where T: class` – cuongle Jan 25 '15 at 07:16
  • 1
    A "reference type" is one that is defined using `class`. A "value type" is defined using `struct` or `Structure`. Only reference types can be nulls. (Except for the magical type Nullable(of T).) – Jonathan Allen Jan 25 '15 at 07:49
  • 2
    The VB keyword `Nothing` is spelled `default(T)` in C#. They do exactly the same thing in all cases. (Also note that Nothing isn't the same as Null. All Nulls are Nothing, but not all Nothings are Null.) – Jonathan Allen Jan 25 '15 at 07:52
  • 1
    @cuongle - consider adding c# 7.1 `return default;` syntax as discussed in https://meta.stackoverflow.com/questions/391753/is-it-advisable-to-edit-a-third-party-answer-to-use-new-language-features – Alexei Levenkov Nov 30 '19 at 18:41
0

In case of C# you have to use default(T) and it will return 0 not Nothing or Null if you just return T and not T?.

Now In case of VB.net as per your example you say that you return Nothing which is correct but did you try to check return value of that function.

Like

Dim c = <<your class object>>.FindItem("test",Nothing);  // you have to pass valid values.

If you check value of C then it is 0 and not nothing.

Now comes to what happen at MSIL level.

When you compile you VB.net code and check its MSIL. you will find initobj !T and this is instruction for default(T). It is doing this automatically. So if you call your VB.net library from C# then also it will work.

dotnetstep
  • 17,065
  • 5
  • 54
  • 72
  • Yes, Nothing in VB is the default value. The default value for reference types is null. So it is for nullable value types. So returning default(T) in C# instead of Nothing is appropriate. – Drunken Code Monkey Jan 25 '15 at 05:35
0

You can used this method

public static IList<int> ToIntList(this object values)
        {
            //try to convert using modified ChangeType which handles nullables
            try
            {
                if (values == null) return new List<int>();

                var charValues = values.ToType<string>();
                return charValues.Split(',').Select(n => Convert.ToInt32(n)).ToList();
            }
            //if there is an exception then get the default value of parameterized type T
            catch (Exception)
            {
                return new List<int>();
            }
        }

internal static object ChangeType(object value, Type conversionType)
        {
            if (conversionType == null)
            {
                throw new ArgumentNullException("conversionType");
            } 

            if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition() == typeof (Nullable<>))
            {
                if (value == null)
                {
                    return null;
                } 
                var nullableConverter = new NullableConverter(conversionType);
                conversionType = nullableConverter.UnderlyingType;
            }
            return Convert.ChangeType(value, conversionType);
        }

Used like this

userVar.ToType<int>()
userVar.ToType<string>()
Pablo Martinez
  • 115
  • 1
  • 5