In short, you simply can't... the language spec doesn't permit overloads
separated purely by constraints.
But you can just use null! The following does exactly what you want...
int? a = null;
string b = null;
if (a != null) {...}
if (b != null) {...}
In the case of generics, default(T) would also return either a null (class),
an empty-nullable (nullable-T) or a zero-value (struct).
Note that there appear to be some minor JIT optimisation issues with
unconstrained generic use of null tests, but even so, within a generic with
"T foo", testing foo against null will do the right thing for all 3 cases.
For some specific cases (benchmarked etc), I do have a suitable IsNull
method tucked away... but I honestly don't think you need it! If you let me
know more about what you are trying to do (and whay is/isn't working) I can
show you how it works... but I don't want to post it without reason, as it
might add more confusion than necessary...
Marc
Well, in my scenario, I am implementing a method similar to Left Outer
Join. I want all the elements from the outer list, even if there is
not a matching inner list element. I can easily make one of the types
Nullable, but then the rest of the arguments become more difficult.
For instance, I can't pass in Comparer<int>.Default.Compare anymore
because one of the arguments is Nullable. So I have to write method
like this:
delegate(int lhs, int? rhs)
{
return lhs < rhs ? -1 : rhs < lhs ? 1 : 0;
}
and instead of passing in two int[] I have to pass in an int?[] which
is just cumbersome.
That is why I was hoping for the overloading. There is no point in
making a generic method if it is so complicated to get the syntax
right that it ends up being longer than writing the method over again.
Here is my example method:
/// <summary>
/// Performs a left outer join on ranges sorted by the key
value.
/// </summary>
/// <remarks>The collections must be sorted by key.</remarks>
public static IEnumerable<KeyValuePair<TFirst, TSecond>>
LeftOuterJoin<TFirst, TSecond>(IList<TFirst> lhs,
IList<TSecond> rhs,
Comparison<TFirst, TSecond> keyComparison)
{
Debug.ArgumentNull(lhs, "lhs");
Debug.ArgumentNull(rhs, "rhs");
Debug.ArgumentNull(keyComparison, "keyComparison");
int lhsFirst = 0;
int rhsFirst = 0;
int position = rhsFirst;
bool found = false;
while (lhsFirst != lhs.Count)
{
int result = keyComparison(lhs[lhsFirst],
rhs[position]);
if (result < 0)
{
if (!found)
{
yield return new KeyValuePair<TFirst,
TSecond>(lhs[lhsFirst], default(TSecond));
}
else
{
found = false;
}
++lhsFirst;
position = rhsFirst;
}
else if (result > 0)
{
++position;
rhsFirst = position;
}
else
{
yield return new KeyValuePair<TFirst,
TSecond>(lhs[lhsFirst], rhs[position]);
++position;
found = true;
}
if (position == rhsCount)
{
position = rhsFirst;
if (!found)
{
yield return new KeyValuePair<TFirst,
TSecond>(lhs[lhsFirst], default(TSecond));
}
else
{
found = false;
}
++lhsFirst;
}
}
}
To call this with char, char? looks like this:
char[] lhs = new char[] { 'A', 'B', 'E', 'F' };
char?[] rhs = new char?[] { 'A', 'A', 'B', 'C', 'D',
'F' };
IEnumerable<KeyValuePair<char, char?>> pairs
= Algorithms.LeftOuterJoin<char, char?>(lhs, rhs,
delegate(char l, char? r)
{
return l < r ? -1 : r < l ? 1 : 0;
});
List<KeyValuePair<char, char?>> result = new
List<KeyValuePair<char, char?>>(pairs);
It's very uncomfortable.