can anyone explain why this is allowed:
class A1
{
public virtual T? F1<T>(T? t) where T : struct { throw new NotImplementedException(); }
public virtual T? F1<T>(T? t) where T : class { throw new NotImplementedException(); }
}
while this is not:
class A1
{
public virtual T? F1<T>(T t) where T : struct { throw new NotImplementedException(); }
public virtual T? F1<T>(T t) where T : class { throw new NotImplementedException(); }
}
"Type 'A1' already defines a member called 'F1' with the same parameter types"
the difference is that in the second case the arguments are not nullable.
how comes that the signatures are same if in one case it's for value types and in another case for reference types?
can anyone explain why this is allowed:
class A1
{
public virtual T? F1<T>(T? t) where T : struct { throw new NotImplementedException(); }
public virtual T? F1<T>(T? t) where T : class { throw new NotImplementedException(); }
}
while this is not:
class A1
{
public virtual T? F1<T>(T t) where T : struct { throw new NotImplementedException(); }
public virtual T? F1<T>(T t) where T : class { throw new NotImplementedException(); }
}
"Type 'A1' already defines a member called 'F1' with the same parameter types"
the difference is that in the second case the arguments are not nullable.
how comes that the signatures are same if in one case it's for value types and in another case for reference types?
Share Improve this question edited Jan 25 at 13:17 Aliq Qim asked Jan 18 at 13:33 Aliq QimAliq Qim 1369 bronze badges 2 |3 Answers
Reset to default 1T?
means different things, depending on whether T
is a value type or reference type.
When T
is a value type, T?
is syntactic sugar for System.Nullable<T>
. When T
is a reference type, T?
is still T
, as far as the runtime is concerned. Nullable reference types is purely a feature of the compiler. It boils down to the compiler doing some compile-time checks to prevent NullReferenceException
s at runtime.
So really you have just declared two methods taking completely different parameter types. One takes a System.Nullable<T>
, and the other takes a reference type T
that is annotated at compile-time to be nullable.
If both methods take a parameter of type T
, then obviously they are exactly the same except they have different type constraints. Method overloads that differ only in type constraints are not allowed.
You are getting the following compiler error pointing at the second F1
overload:
CS0111: Type 'A1' already defines a member called 'F1' with the same parameter types
This is because the signature is given by the method name and parameter types only. The return type is not part of the signature when it comes to overload resolution. You cannot overload a method only differing in the return type.
In the case of the T? t
parameters, the two mean different things for classes and structs.
- in
T? t
,where T : struct
:T?
meansNullable<T>
. - in
T? t
,where T : class
:T?
meansT
where the compiler allowsnull
to be passed as an argument. ButT?
is not another type thanT
. I.e.,typeof(T?)
(which does not compile) is the same astypeof(T)
(this is the very reason the first one does not compile).
See also:
- Method signatures (C# Programming Guide)
- C# in Depth / Overloading see "Return types" (Jon Skeet)
I guess my example can be boiled down to this:
class A1
{
public void F1<T>(T t) where T : struct { throw new NotImplementedException(); }
public void F1<T>(T t) where T : class { throw new NotImplementedException(); }
}
and the core question would be why doesn't compiler treat generic for structs and generic for class as different param types. I guess there is no logical explanation, it just doesn't. or am I missing something?
`1
) and everything else (including the nameT
, and any constraints) is secondary details – Marc Gravell Commented Jan 18 at 13:47