2
public class Node<E> : IPosition<E>
{
    private E element;
    public Node<E> PrevNode { get; set; }
    public Node<E> NextNode { get; set; }

    //Constructor
    public Node(E e, Node<E> p, Node<E> n)
    {
        element = e;
        PrevNode = p;
        NextNode = n;
    }
}

I have the above Node class which I want when I create a new node object, to be able to do this:

Node<E> n = new Node<E>(null, null, null);

This does not work since all types cannot be null in C#. What surprises me is when I try a similar thing in Java, it works! I have seen some relevant questions on Stack Overflow, but they do not give the result I want. I do not want to use the default(E).

panoskarajohn
  • 1,846
  • 1
  • 21
  • 37
  • C# allows for generics to be used with primitive types, while Java only allows reference types with generics. All reference types can be null, so it works in Java. – 4castle Jun 29 '17 at 18:03
  • 1
    `Node n = new Node(null, null, null);` you have to provide the specific type in order that to work. – dcg Jun 29 '17 at 18:03
  • 2
    `I do not need the default(E)` Don't you though? How is that not *exactly* what you want? – Servy Jun 29 '17 at 18:04
  • Or something like `Node n = new Node(null, null, null);` where the type is nullable. – Jay Buckman Jun 29 '17 at 18:07
  • @Derek Not if that code is in the definition of `Node`; inside the class definition there *is* a type named `E`. – Servy Jun 29 '17 at 18:12
  • @Forecast So what do you *want* to have happen in the case of an integer? You can't pass null because it's not a nullable type. – Servy Jun 29 '17 at 19:39
  • @Servy if i use the where E : class i can. – panoskarajohn Jun 29 '17 at 19:56
  • @Forecast No, if you do that then *you can't use an `int` at all*. You've simply made the code cease to compile if anyone uses a non-nullable type, rather than allowing non-nullable types to have a null value. – Servy Jun 29 '17 at 20:01
  • @Servy how can i achieve that? I want to be able to pass non-nullable types...? – panoskarajohn Jun 29 '17 at 20:21
  • 1
    @Forecast Well you accepted an answer that prohibits you from ever using non-nullable types. You shouldn't do that if you want to actually be able to use non-nullable types. If you *do* want to be able to use non-nullable types, you need to describe what you want to happen. They can't have a `null` value (by definition, because they're a non-nullable type). What value *do* you want them to have? – Servy Jun 29 '17 at 20:23
  • @Servy well imagine building a linked list..i wanna be able to have some kind of unification in my code in order to avoid extra checks. For example in a linked list, the header i want it to be null. – panoskarajohn Jun 29 '17 at 20:26
  • That's not really enough information to give you an answer. – Servy Jun 29 '17 at 20:28
  • @Servy ok how the List l1 = new List() works? and it is possible for all data types at the same time string etc? – panoskarajohn Jun 29 '17 at 20:33
  • 1
    @Forecast You can look at the source code for a `List` yourself. It never tries to store any `null` value in anything of type `T`, it only ever stores values of that type, which is what you should be doing as well. – Servy Jun 29 '17 at 20:34
  • @Servy ok i did roughly.What if i changed my node constructor to accept Node(Object e, ...)? What kind of implications would that have to nullable types?. – panoskarajohn Jun 29 '17 at 20:41
  • 1
    @Forecast Your type is now no longer generic, and you've lost all static type checking. – Servy Jun 29 '17 at 20:44
  • @Servy Thank you for your guidance this requires some research...i will not waste your time further. This is enough for me to go on. – panoskarajohn Jun 29 '17 at 20:47
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/148051/discussion-between-forecast-and-servy). – panoskarajohn Jun 30 '17 at 11:46

3 Answers3

11

You need a generic type constraint stating that E must be a reference type:

public class Node<E> : IPosition<E> where E : class

That is, unless you also need E to be a value type for other reasons. If that's the case, you need to sacrifice one requirement or the other.

Nullable value types are an option: With your original version, lacking the type constraint (because Nullable<T> is a value type itself), you could use int?. The following code compiles for me without the constraint:

var y = new Node<int?>(null, null, null);

int? isn't int, but it isn't exactly not int, is it?

2

What surprises me is when i try something similar in Java, it works.

This is because Java generic types are implemented with type erasure, which effectively means that they are all java.lang.Object descendants.

For example, you cannot use primitive int as your Node's type argument in Java: you are forced to use java.lang.Integer instead. Hence, element can be assigned null regardless of T.

In C# there is no such limitation on type parameter: writing Node<int> is perfectly legal. However, with element of type int you can no longer write element = null, which is the root cause of the error that you see.

In addition to the default(T) approach that you mention, you could require that T is a reference type, like this:

public class Node<E> : IPosition<E> where E : class {
    ...
}

Now it is legal to pass null to the initial parameter of Node's constructor, but it is illegal to instantiate Node<T> with any value type, including Nullable<Tx>.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
1

What if we do like this:

First of all create one simple interface

public interface IOptional<T>: IEnumerable<T> {}

And write implementation of it

public class Maybe<T>: IOptional<T>
{
    private readonly IEnumerable<T> _element;
    public Maybe(T element)
        : this(new T[1] { element })
    {}
    public Maybe()
        : this(new T[0])
    {}
    private Maybe(T[] element)
    {
        _element = element;
    }
    public IEnumerator<T> GetEnumerator()
    {
        return _element.GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

After this we do some changes in your Node class

public class Node<E> : IPosition<E>
{
    private IOptional<E> element;
    public Node<E> PrevNode { get; set; }
    public Node<E> NextNode { get; set; }

    //Constructor
    public Node(IOptional<E> e, Node<E> p, Node<E> n)
    {
        element = e;
        PrevNode = p;
        NextNode = n;
    }
}

And use it

Node<E> n = new Node<E>(
                new Maybe<E>(), 
                null, 
                null
             );

No more null checks inside Node class on this field

Instead of this

if (this.element != null) { .. }

Write like this

this.element.Select(e => { doSomething(e); return true; })

like this

if (this.element.Any()) 
{ 
    var elem = this.element.First();
    // do something
}

or write one small extension method

public static IOptional<TOutput> Match<TInput, TOutput>(
    this IEnumerable<TInput> maybe,
    Func<TInput, TOutput> some, Func<TOutput> nothing)
{
    if (maybe.Any())
    {
        return new Maybe<TOutput>(
                    some(
                        maybe.First()
                    )
                );
    }
    else
    {
        return new Maybe<TOutput>(
                    nothing()
                );
    }
}

and do like this

var result = this.element
                 .Match(
                     some: e => e.ToString(),
                     nothing: () => "Ups"
                 )
                 .First();
giokoguashvili
  • 2,013
  • 3
  • 18
  • 37