Roslyn: Proposal: default constructor

Created on 2 Jan 2017  路  5Comments  路  Source: dotnet/roslyn

Proposal: default constructor

Problem

Looking at the C# features status we can see interest of [non-nullable reference types]. Syntax may look like:

string! a = "Hello, world"; //non-nullable reference type

It's a simple way to solve "The Billion Dollar Mistake", but [there is a known issue] that many of generic methods may return default of type T.
How can we solve this problem without throwing any exception?

Solution: default constructor

C# should support the ability to create an instance of class by using default(T). Now you can change the behavior of default using following code :

public class Person
{
        public bool IsAvalible { get; }
        public bool Name { get; }
        ...
        public default Person() // new Person() is still avalible
        {
            IsAvalible = false;
            Name = "";
            ...
            Log($"A default instance of {nameof(Person)} was created");
        }

        public Person() // note, that doesn't fade the default constructor
        {
            IsAvalible = true;
            Name = GetNameFromDB(...);
            ...
        }

If some class implements public default constructor, it may non-nullable with default:

Person! p = default(Person);
...
Person! p1 = persons.FirstOrDefault();
...
Person! p2; // even that is okay, because compiler will call default(Person)
bool b = p2.IsAvalible; // false

In case you need to cross implementation of constructor with your default constructors use this(default).

public Person() : this(default) { ... }

or, in another case:

public default Person() : this() { ... }

Similarly, base(default) syntax allows us to call default constructor of base class:

public class Person : PersonBase
{
    public default Person() : 
        base(default) // compilation error if PersonBase didn't implement public/protected default constructor
    {
        ...
    }

There is another reason to add this feature. Using instances by default we can simplify "writing Non-null object" pattern.

public class NonNullList : List<int>
{
    public NonNullList() : base() { }

    public default NonNullList() : base()
    {
        this.Add(42);
        Log($"A default instance of {nameof(NonNullList)} was created");
    }

```csharp
var l1 = default(NonNullList).Count; // 1

Sure, in case some class doesn't implement default constructor following code will throw an error
```csharp
SomeClass! p = default(SomeClass); //throws NullCastException
Area-Language Design Discussion

Most helpful comment

The current plans for non-nullable reference types does not use the Type! syntax. Nor does it throw any exceptions or introduce NullCastException. The most difficult to solve problem doesn't arise from explicit use of default(T), but from the default value of a field (or array element) of type T, which begins its life with the value default(T). Adding this proposed feature doesn't solve that, because (as the language is defined today) it is possible to observe a field during construction before the field has been initialized. Such a field of non-nullable type Type would be observed to be null.

This proposal doesn't address how the C# compiler can produce the code for the generic case such that the runtime invokes the initializing method you suggest for source code default(T). Without a way to generate code, the feature is a non-starter. If you are proposing a CLI spec change to go along with this suggestion, then it is potentially a very complex feature, and would require work in every virtual machine implementation.

All 5 comments

This could only work when used specifically with default and the exact type. It still wouldn't solve any of the actual problems with the default value of reference types anywhere else, particularly with arrays or generics. default(T) could never resolve to default(NonNullList) at compile time.

There is no problem with arrays or generics

public class SomeGeneric<T>
    {
        public T Value { get; }
        public bool IsCreated { get; }
        public default SomeGeneric()
        {
            IsCreated = true;
            Value = default(T);
        }
    }
//Now we may use it in default context
SomeGeneric<int> a = default(SomeGeneric<int>);
Console.WriteLine(a.Value); // 0

SomeGeneric<Person> b = default(SomeGeneric<Person>);
Console.WriteLine(b.Value); // equivalent to default(Person)

SomeGeneric<string> c = default(SomeGeneric<string>);
Console.WriteLine(c.Value); // null

With n-dimensional arrays C# may create an empty array by rank n, if there is a overloaded default constructor:

Person![,] array = default(Person![,]); // equivalent to new Person![0, 0];

There is no problem with arrays or generics

Saying so doesn't make it so. There will be such a problem because any array of reference types will always default to null as that is what the CLR will do and C# cannot override that behavior. The same is true with generics. In your example above SomeGeneric<Person>.Value can only ever result in null because there is no way for either the C# compiler or the CLR to resolve any custom method (or constructor). There can only be one implementation of the IL for a generic type/method.

This proposal is effectively a dupe of #71 and #5627, both of which have been closed.

The current plans for non-nullable reference types does not use the Type! syntax. Nor does it throw any exceptions or introduce NullCastException. The most difficult to solve problem doesn't arise from explicit use of default(T), but from the default value of a field (or array element) of type T, which begins its life with the value default(T). Adding this proposed feature doesn't solve that, because (as the language is defined today) it is possible to observe a field during construction before the field has been initialized. Such a field of non-nullable type Type would be observed to be null.

This proposal doesn't address how the C# compiler can produce the code for the generic case such that the runtime invokes the initializing method you suggest for source code default(T). Without a way to generate code, the feature is a non-starter. If you are proposing a CLI spec change to go along with this suggestion, then it is potentially a very complex feature, and would require work in every virtual machine implementation.

I suggest to use #define NOTNULLABLE for now and use ? to declare nullable and keep it consistent with value types.

In next release it could be defined by default, so to run old code you would have to add #undef NOTNULLABLE and compiler should respect old ways for old code.

Was this page helpful?
0 / 5 - 0 ratings