How to allow a generic type parameter for a C# method to accept a null argument?

人走茶凉 提交于 2019-12-30 04:43:10

问题


private static Matcher<T> EqualTo<T>(T item)
{
    return new IsEqual<T>(item);
}

How do I modify the above method definition such that the following are valid/allowed.

EqualTo("abc");
EqualTo(4);
EqualTo(null); // doesn't compile. EqualTo<string>(null) does

Trying to port some Java code where null seems to be acceptable value for a T parameter.


Update
Thanks: for all the answers - especially Eamon and Jason. I didn't want the method calls to bother with type-inference. The following overload fixed it.

    private static Matcher<object> EqualTo(object item)
    {
        return EqualTo<object>(item);
    }  

Actually the above question was a part of a larger puzzle. The end goal was for the following to work.

        this.AssertThat(null, EqualTo(null));
        this.AssertThat(null, Not(EqualTo("hi")));
        this.AssertThat("hi", Not(EqualTo(null)));

Applied the same fix.. RFC. (Ignore the ugly extension method part - that's another problem. Wanted to have these methods in all test-fixtures without inheritance.)

public static void AssertThat<T>(this object testFixture, object actual, Matcher<T> matcher, string message = "")
{
  AssertThat(anyObject, (T)actual, matcher, message);
}

public static void AssertThat<T, TSuper>(this object testFixture, T actual, Matcher<TSuper> matcher, string message = "") where T : TSuper
{
  ... check and assert

回答1:


Consider the following method:

public bool IsNullString<T>(T item) {
    return typeof(T) == typeof(string) && item == null;
}

Yes, this is a pathetically stupid method and using generics is pointless here, but you'll see the point in a moment.

Now consider

bool first = IsNullString<string>(null);
bool second = IsNullString<Foo>(null);

bool third = IsNullString(null);

In the first and second, the compiler can clearly distinguish the type of T (no inference is needed). In the third, how the compiler infer what T is? In particular, it can't distinguish between T == string and T == Foo, or any other type for that matter. Therefore, the compiler has to give you a compile-time error.

If you want to get around this, you either need to cast null

EqualTo((object)null);

or explicitly state the type

EqualTo<object>(null)

or define an overload

private static Matcher<object> EqualTo(object item) {
    return new IsEqual<object>(item);
}



回答2:


Not possible without explicitly specifying a T or doing a cast. Generics are compile time constructs and as such if the compiler can't figure out the type at compile time, then it won't compile (as you're seeing).




回答3:


Since you can't do exactly what you are wanting to do, how about defining an EqualTo(object) overloaded method? That should allow your required syntax.




回答4:


You may work around this limitation by using the following syntax:

EqualTo("abc");
EqualTo(4);
EqualTo(default(object));
//equivalently:
EqualTo((object)null);

default(T) is the value a field of type T has if not set. For reference types, it's null, for value types it's essentially memory filled with zero bytes (...which may mean different things for different types, but generally means some version of zero).

I try to avoid the null everywhere in my code nowadays. It hampers type inference elsewhere too, such as with the var declared field and in a ternary operator. For example, myArray==null ? default(int?) : myArray.Length is OK, but myArray==null ? null : myArray.Length won't compile.




回答5:


Maybe implementing a non-generic EqualTo, which takes an Object as the argument type, would solve the issue of rewriting those code lines.



来源:https://stackoverflow.com/questions/5612489/how-to-allow-a-generic-type-parameter-for-a-c-sharp-method-to-accept-a-null-argu

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!