Duck Typing in the C# Compiler

Duck typing in the C# compiler

There's nothing special about IDisposable here - but there is something special about iterators.

Before C# 2, using this duck type on foreach was the only way you could implement a strongly-typed iterator, and also the only way of iterating over value types without boxing. I suspect that if C# and .NET had had generics to start with, foreach would have required IEnumerable<T> instead, and not had the duck typing.

Now the compiler uses this sort of duck typing in a couple of other places I can think of:

  • Collection initializers look for a suitable Add overload (as well as the type having to implement IEnumerable, just to show that it really is a collection of some kind); this allows for flexible adding of single items, key/value pairs etc
  • LINQ (Select etc) - this is how LINQ achieves its flexibility, allowing the same query expression format against multiple types, without having to change IEnumerable<T> itself
  • The C# 5 await expressions require GetAwaiter to return an awaiter type which has IsCompleted / OnCompleted / GetResult

In both cases this makes it easier to add the feature to existing types and interfaces, where the concept didn't exist earlier on.

Given that IDisposable has been in the framework since the very first version, I don't think there would be any benefit in duck typing the using statement. I know you explicitly tried to discount the reasons for having Dispose without implementing IDisposable from the discussion, but I think it's a crucial point. There need to be good reasons to implement a feature in the language, and I would argue that duck typing is a feature above-and-beyond supporting a known interface. If there's no clear benefit in doing so, it won't end up in the language.

Can the C# compiler use duck typing with foreach over a generic type?

No, basically. The only use for "B" is when the TList itself is actually a struct; the IL can then use "constrained call" to call the original GetEnumerator() without any part having to box the original struct TList value.

But: once you've called GetEnumerator(), you're back into IEnumerator<T> land, and it will not use the custom iterator.

All of which is mostly moot in this case, because iterator blocks are also fairly "allocatey". So... if avoiding boxing the TList is your concern, you are presumably obsessive about allocations: in which case you wouldn't write the iterator block this way, either.

What is Interface Duck Typing?

Duck typing allows an object to be passed in to a method that expects
a certain type even if it doesn’t inherit from that type. All it has
to do is support the methods and properties of the expected type in
use by the method. I emphasize that last phrase for a reason. Suppose
we have a method that takes in a duck instance, and another method
that takes in a rabbit instance. In a dynamically typed language that
supports duck typing, I can pass in my object to the first method as
long as my object supports the methods and properties of duck in use
by that method. Likewise, I can pass my object into the second method
as long as it supports the methods and properties of rabbit called by
the second method. Is my object a duck or is it a rabbit? Like the
above image, it’s neither and it’s both. In many (if not most) dynamic
languages, my object does not have to support all methods and
properties of duck to be passed into a method that expects a duck.
Same goes for a method that expects a rabbit.It only needs to support
the methods and properties of the expected type that are actually
called by the method.

Please refer this to get an idea about Duck Typing

http://haacked.com/archive/2007/08/19/why-duck-typing-matters-to-c-developers.aspx/

When to use Duck Typing?

C# has strong typing for a reason. Unless you have a valid reason (such as needing COM interop) to use the dynamic type, you should probably avoid it like the plague, or you'll risk turning compile-time problems into runtime problems. dynamic is powerful, but easy to abuse. Think long and hard about if you really need dynamic typing - if you think there is, there's a chance that you're approaching the problem wrong to begin with, and need to refactor your code.

To specifically answer your question - one potential use case was if you were writing serialisation code and needed to accept a deserialised object, for example a deserialised JSON structure from a web API request. Such methods would need to handle any type given to them, which is a situation in which using dynamic is better than the alternative (ie. a truck-load of reflection).

Another example I can think of would be interoperability with languages specifically on the Dynamic Language Runtime (eg. JavaScript, IronPython, IronRuby, ...), and needed to write a method accepting types from such languages.

From Beginning Visual C# 2012 Programming:

For most C# code that you write, avoid the dynamic keyword. However, if a situation arises where you need to use it, use it and love it - and spare a thought for those poor programmers of the past who didn't have this powerful tool at their disposal.

Using Java and C#'s generics to simulate duck typing

can C#'s generics actually achieve the same thing as duck typing?

No. But C#'s generics can include a constraint where the type parameter is restricted to inherit or implement some particular type. When that's done, any expression of the type of that type parameter is resolved as the constrained type and members from that type can be accessed.

This is similar to the extends constraint described in the article you read.

The only duck-typing support in C# is the dynamic keyword, where final compilation of expressions involving dynamic values is deferred until runtime when the actual runtime type is known.

Related reading:

Trivial C# class with a generic parameter wouldn't compile for no apparent reason

Call a method of type parameter

Implementing safe duck-typing in C#

How can you know if a cow walks like a duck and quacks like a duck if you don't have a living, breathing cow in front of you?

Duck-typing is a concept used at run-time. A similar concept at compile-time is structural typing which is AFAIK not supported by the CLR. (The CLR is centred around nominative typing.)

[A structural type system] contrasts with nominative systems, where comparisons are based on explicit declarations or the names of the types, and duck typing, in which only the part of the structure accessed at runtime is checked for compatibility.

The usual way to ensure that duck-typing throws no exception at run-time are unit-tests.

Reusing the same function by casting, or duck typing different object types

Off the top of my head, your options are:

  1. Get both of the source classes to implement the same interface and pass that around instead of the concrete types. This would be the preferable option but I'm guessing not possible here.
  2. Deserialize and serialize back to convert between types. You already have this code, but like you say it may be slow.
  3. Use a mapping library such as AutoMapper to convert between types. This is very fast but requires you bring in an external library from Nuget (I've used AutoMapper many times)
  4. Manually map the properties yourself. This would probably be the fastest code but pretty awful to write.
  5. Use dynamic all the way down the chain, not just at the top. You lose compile time type checking, but it should be reasonably fast. For example, instead of having a function like this:

    public static string _ExtractFoo(ProductService.Customer customer)
    {
    return customer.DoSomethingExciting();
    }

    You would have this:

    public static string _ExtractFoo(dynamic customer)
    {
    return customer.DoSomethingExciting();
    }

    You could, if you wanted, add some checking to ensure that customer is either ProductService.Customer or CoreService.Customer if you want some safety.

Duck typing doesn't implicitly convert a type to an interface variable

Unfortunately, C# or .NET does not support duck typing in this particular context.

For a type to implement a particular interface, it will have to explicitly declare that it implements this interface. You say "satisfy" now, but this has no meaning for C# or .NET.

Does the type MyClass implement IInterface? No, sadly it doesn't.

This declaration:

public class MyClass { }

Declares a class that inherits System.Object, and does not implement any interface.

The fact that the interface contains no methods or properties does not in any way make it match up with this type. The class still does not implement this interface.

The only way for a class to implement an interface is to make it explicit:

public class MyClass : IInterface { }

So no, there is no way to force C# or .NET to consider that class as one implementing this interface.

The common ways to handle this is to make a wrapper class (ie. a class that implements IInterface and contains a MyClass, delegating all methods and/or properties to the contained MyClass instance), or, you know, actually implement the interface.

To conclude, the only way to make this code compile:

IInterface myVariable = new MyClass();

is to make MyClass explicitly either implement the interface:

class MyClass : IInterface { }

or to inherit from another class that implements it:

class BaseClass : IInterface { }
class MyClass : BaseClass { }

duck typing in D

This is actually a very common thing to do in D. It's how ranges work. For instance, the most basic type of range - the input range - must have 3 functions:

bool empty();  //Whether the range is empty
T front(); // Get the first element in the range
void popFront(); //pop the first element off of the range

Templated functions then use std.range.isInputRange to check whether a type is a valid range. For instance, the most basic overload of std.algorithm.find looks like

R find(alias pred = "a == b", R, E)(R haystack, E needle)
if (isInputRange!R &&
is(typeof(binaryFun!pred(haystack.front, needle)) : bool))
{ ... }

isInputRange!R is true if R is a valid input range, and is(typeof(binaryFun!pred(haystack.front, needle)) : bool) is true if pred accepts haystack.front and needle and returns a type which is implicitly convertible to bool. So, this overload is based entirely on static duck typing.

As for isInputRange itself, it looks something like

template isInputRange(R)
{
enum bool isInputRange = is(typeof(
{
R r = void; // can define a range object
if (r.empty) {} // can test for empty
r.popFront(); // can invoke popFront()
auto h = r.front; // can get the front of the range
}));
}

It's an eponymous template, so when it's used, it gets replaced with the symbol with its name, which in this case is an enum of type bool. And that bool is true if the type of the expression is non-void. typeof(x) results in void if the expression is invalid; otherwise, it's the type of the expression x. And is(y) results in true if y is non-void. So, isInputRange will end up being true if the code in the typeof expression compiles, and false otherwise.

The expression in isInputRange verifies that you can declare a variable of type R, that R has a member (be it a function, variable, or whatever) named empty which can be used in a condition, that R has a function named popFront which takes no arguments, and that R has a member front which returns a value. This is the API expected of an input range, and the expression inside of typeof will compile if R follows that API, and therefore, isInputRange will be true for that type. Otherwise, it will be false.

D's standard library has quite a few such eponymous templates (typically called traits) and makes heavy use of them in its template constraints. std.traits in particular has quite a few of them. So, if you want more examples of how such traits are written, you can look in there (though some of them are fairly complicated). The internals of such traits are not always particularly pretty, but they do encapsulate the duck typing tests nicely so that template constraints are much cleaner and more understandable (they'd be much, much uglier if such tests were inserted in them directly).

So, that's the normal approach for static duck typing in D. It does take a bit of practice to figure out how to write them well, but that's the standard way to do it, and it works. There have been people who have suggested trying to come up with something similar to your Implements!(S, Interface) suggestion, but nothing has really come of that of yet, and such an approach would actually be less flexible, making it ill-suited for a lot of traits (though it could certainly be made to work with basic ones). Regardless, the approach that I've described here is currently the standard way to do it.

Also, if you don't know much about ranges, I'd suggest reading this.



Related Topics



Leave a reply



Submit