Yes, you are. Generic wildcards enforce that the container contains "something, but we're not sure what" and raw types are just "the container contains objects".
So you can get some counterintuitive results,
List<?> wild = new ArrayList<String>();
List raw = new ArrayList<String>();
wild.add(1); // Compile time error
raw.add(1);
raw.add("Foo");
wild.add("string"); // Still a compile time error
Wild cards are represented as the same as raw
at run time, but they are typechecked to make sure you never do something silly, like adding random objects to it.
So if you don't care about type safety, then yes, you can go ahead and use List
, but for 3 characters, I'd just use the parameterized type.
As others have said, this isn't possible, but there are a couple of easy constructs you can create yourself that achieve similar aims.
Maybe<T>
public struct Maybe<T> where T : class
{
public bool HasValue { get; private set; }
private readonly T _value;
public T Value
{
get
{
if(!HasValue){throw new InvalidOperationException();}
return _value;
}
}
public Maybe(T value) : this()
{
if(value==null) { throw new ArgumentNullException("value"); }
HasValue = true;
_value = value;
}
}
(This could also be implemented other ways, such as an IEnumerable<T>
with the constraint that it has exactly 0 or 1 items)
NotNull<T>
public class NotNull<T> where T : class
{
public readonly T Value;
public NotNull(T value)
{
if(item == null){ throw new ArgumentNullException("value"); }
Value = value;
}
}
Maybe<T>
essentially just reimplements Nullable<T>
for reference types. It has similar advantages and disadvantages to the ones described in your post. It's probably only valuable if you have a convention throughout your project to use it for any value where null
is a valid value.
NotNull<T>
is likely to be more directly what you want, as MainMa stated. By passing around values wrapped in this type you can remove the need to check whether a value is null in a guard clause. However, there's no way to completely prevent a NotNull<T>
from itself being null. You can't make it a struct because of the requirement of a default constructor, which would leave Value
null.
(EDIT: As others have pointed out, in C# 6 you will be able to have a parameterless constructor for a struct, but it will not be used when initializing arrays or getting the result of default(T)
, so you still can't really rely on the guarantee that Value
won't be null)
So while both of these potentially add some value in signalling whether null
should be considered valid or not, they're both limited in that they require constant adherence to a convention, in addition to the boilerplate and readability issues. Whether the trade-off is worth it would be a matter of judgement, but remember that code contracts or other static analysis tools also exist as a possible alternative way of addressing this issue.
Best Answer
For an authoritative answer, I would refer you to Eric Lippert's answer to that question on StackOverflow a few years ago, a snippet of which is briefly quoted below.