Home > Mobile >  After instantiating via parameterless constructor declared in type constraint, assignment modifies h
After instantiating via parameterless constructor declared in type constraint, assignment modifies h

Time:11-04

In the code below, stepping through the debugger, observe that MyWrapperInstance, an instance of Child.Wrapper, has only the hidden field from Base.BaseWrapper populated as an instance of Child.Node instantiated via the new() constructor from the type constraint, so that MyWrapperInstance.Node still evaluates to null.

I am somewhat new to C# but this went against my intuition. Is there an explanation for why this works this way?

var MyChildInstance = new Child();
var MyWrapperInstance = MyChildInstance.New();
// evaluates to true:
var isNull = MyWrapperInstance.Node is null;

public class Base
{
    public class BaseNode { }
    public class BaseWrapper
    {
        public BaseNode Node;
    }
    public TWrapper MakeNew<TWrapper, TNode>()
      where TWrapper : BaseWrapper, new()
      where TNode : BaseNode, new()
    {
        var wrapper = new TWrapper()
        {
            Node = null
        };
        // I expected this to write the derived class's property
        // but it seems to write the hidden field of base class
        // instead
        wrapper.Node = new TNode();

        return wrapper;
    }
}

public class Child : Base
{
    public class Node : BaseNode { }
    public class Wrapper : BaseWrapper
    {
        // Hides BaseWrapper Property
        public new Node Node;
    }

    public Wrapper New() => MakeNew<Wrapper, Node>();
}

Note: I created a new Console application in Visual Studio 17.3 using .NET 6.0 and this is the content of Program.cs.

Expectation: MyWrapperInstance.Node is not null, and the hidden property is null instead. I tried to inspect in the debugger that the instantiated wrapper has not somehow been cast to BaseWrapper.

CodePudding user response:

Proper intuition - whenever one uses shadowing (like public new Node Node;) it is the best to assume to access such property the compiler will choose base or derived version at random. Indeed, there are well defined rules and in every case one can reason about the choice compiler will make, but "random" is a good starting point.

In this case compiler sees TWrapper as a type that is at least BaseWrapper but knows nothing about any derived classes. So inside generic code wrapper.Node will always be BaseWrapper.Node as compiler must generate all code at compile time and it has no information about essentially unrelated Wrapper.Node.

Note that in most cases when one tries to use new modifier virtual is likely more appropriate - review Difference between shadowing and overriding in C#?.

  • Related