Question

In a similar vein to the blog post by Eric on non nullable reference types, where the solution to the problem seems trivial, but its clearly not.

Why couldn't structural typing be added to c#? What problems would arise if something like the (bad) example below was just dropped into the language?

public structural interface INode
{
 INode Next {get;}
}

class MyLinkedNode { ... } // structurally implements INode
class GraphNode { ... } // this also structurally implements INode

int CountNodes(INode start) 
{
  int i = 0;
  for (INode current = start; current != null; current = current.Next)
   i++;

  return i;
};

void GetCounts()
{
  MyLinedNode node1 = BuildLinkedNodes();
  GraphNode node2 = BuildGraphNodes();

  int count1 = CountNodes(node1);
  int count2 = CountNodes(node2);

  int count3 = CountNodes("hello world"); // compile fail, as string type structure invalid
}
Was it helpful?

Solution

You would lose all the advantages of dynamic polymorphism which are provided by virtual calls and generics.

In the current version of C#, CountNodes (and a hypothetical generic version CountNodes<T> where T : INode) is compiled as a single function, where the access to the Next property has fixed, at compile time, the offset into the v-table of the object which must be used to find the property getter. As a result, new types deriving from INode and defined in other assemblies can be supplied to CountNodes, since they conform to the same v-table layout.

Since the v-table layout is different between MyGraphNode and MyLinkedNode (they are defined without awareness of any required layout), this approach cannot work. The compiler would have to generate new and different code for each actual parameter type. This would limit you to types which existed at compile time and were known at the call site. For example, this would work:

GraphNode node = BuildGraphNodes();
int count = CountNodes(node);

but this would not:

object node = BuildGraphNodes();
int count = CountNodes(node);

Actually, this capability has already been developed. It is called C++ templates. And Microsoft provides a C++/CLI compiler that allows you to use it with .NET. It is even more powerful than the "structural interfaces" you proposed, but it easily accommodates your use case.

template<typename INode>
int CountNodes(INode^ start) 
{
    int i = 0;
    for (INode^ current = start; current != nullptr; current = current->Next)
        ++i;

    return i;
}

(C++/CLI works within the confines of the CLR, and does not require DLR support, quite contrary to what some commenters claimed.)

Given that this ability is already accessible to .NET developers, and would add considerable additional complexity to C#, it is unlikely to pass the cost-benefit analysis of the C# designers. So don't expect C++ templates, or even the lesser "structural interfacing", to ever be part of C#.

If you're willing to forgo all benefits of static type checking, you can use late (lookup by name, not v-table slot) binding in C# today. Just change the parameter type to dynamic. But errors will be delayed until runtime, and performance will also take a significant hit.

OTHER TIPS

Why couldn't structural typing be added to c#?

In general, this would be possible. It would require a completely different form of resolution to be added to the C# language compared to what currently exists.

What problems would arise if something like the (bad) example below was just dropped into the language?

There would be a new set of rules required, as well as an entire different means of overload in the compiler.

That being said, this is definitely possible. F# (today) supports a form of this via statically resolved type parameters, and compiles to the same IL as C#. C++/CLI also supports this type of operation via templates, which also are resolved and handled at compile time.

You can accomplish something similar to this in C# today via dynamic, though you lose all compile time checking.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top