Does C# have an equivalent to Scala's structur

2019-01-27 13:57发布

问题:

In Scala, I can define structural types as follows:

type Pressable = { def press(): Unit }

This means that I can define a function or method which takes as an argument something that is Pressable, like this:

def foo(i: Pressable) { // etc.

The object which I pass to this function must have defined for it a method called press() that matches the type signature defined in the type - takes no arguments, returns Unit (Scala's version of void).

I can even use the structural type inline:

def foo(i: { def press(): Unit }) { // etc.

It basically allows the programmer to have all the benefits of duck typing while still having the benefit of compile-time type checking.

Does C# have something similar? I've Googled but can't find anything, but I'm not familiar with C# in any depth. If there aren't, are there any plans to add this?

回答1:

No, and no plans that I know of. Only named (rather than structural) subtyping (e.g. interfaces).

(Others may want to see also

http://en.wikipedia.org/wiki/Nominative_type_system

http://en.wikipedia.org/wiki/Structural_type_system

)

(A few people may point out some exotic corner cases, like the foreach statement using structural typing for GetEnumerator, but this is the exception rather than the rule.)



回答2:

There isn't a way to define structural types that has a particular function. There is a library that adds duck typing support to C# that can be found here.

This is the example from Duck Typing project. Please note the the duck typing happens at runtime and can fail. It is my understanding also that this library generates proxies for the types that are duck typed, which is far cry from the elegant compile-time support that is enjoyed in Scala. This is most likely as good as it gets with this generation of C#.

public interface ICanAdd
{
    int Add(int x, int y);
}

// Note that MyAdder does NOT implement ICanAdd, 
// but it does define an Add method like the one in ICanAdd:
public class MyAdder
{
    public int Add(int x, int y)
    {
        return x + y;
    }
}

public class Program
{
    void Main()
    {
        MyAdder myAdder = new MyAdder();

        // Even though ICanAdd is not implemented by MyAdder, 
        // we can duck cast it because it implements all the members:
        ICanAdd adder = DuckTyping.Cast<ICanAdd>(myAdder);

        // Now we can call adder as you would any ICanAdd object.
        // Transparently, this call is being forwarded to myAdder.
        int sum = adder.Add(2, 2);
    }
}

This is the C# way of achieving the same thing using the good ol boring interfaces.

interface IPressable {
  void Press();
}

class Foo {
 void Bar(IPressable pressable) {
    pressable.Press();
 }
}

class Thingy : IPressable, IPushable, etc {
 public void Press() {
 }
}

static class Program {
 public static void Main() {
  pressable = new Thingy();
  new Foo().Bar(pressable);
 }
}


回答3:

As others noted, this is not really available in .NET (as this is more a matter of the runtime than a language). However, .NET 4.0 supports similar thing for imported COM interfaces and I believe this could be used for implementing structural typing for .NET. See this blog post:

  • Faking COM to fool the C# compiler

I didn't try playing with this myself yet, but I think it might enable compiler authors to write languages with structural typing for .NET. (The idea is that you (or a compiler) would define an interface behind the scene, but it would work, because the interfaces would be treated as equivalent thanks to the COM equivalence feature).

Also, C# 4.0 supports the dynamic keyword which, I think, could be interpreted as a structural typing (with no static type checking). The keyword allows you to call methods on any object without knowning (at compile-time) whether the object will have the required methods. This is essentially the same thing as the "Duck typing" project mentioned by Igor (but that's, of course, not a proper structural typing).



回答4:

The awaitable pattern in C# can perhaps be interpreted as a limited, ad hoc instance of structural subtyping / existential typing. The compiler will only await objects that have access to a GetAwaiter() method that returns any INotifyCompletion object with a specific set of methods and properties. Since neither the 'awaitable' object nor the 'awaiter' object needs to implement any interface (except INotifyCompletion in the case of the latter), await is similar to a method that accepts structurally typed awaitable objects.