Roslyn: Proposal: C# interface delegation

Created on 21 Sep 2016  Â·  29Comments  Â·  Source: dotnet/roslyn

There was useful feature in Borland Delphi, interface delegation. This feature is absent in C#.
You can delegate interface implementation on class to instance stored in field or property.

Declare sample interface and sample implementation:

``` C#
public interface ISampleInterface
{
void DoSomeWork();
int GetSomeResult();
}

public sealed class SampleImplementation : ISampleInterface
{
public void DoSomeWork()
{
// do some work
}

public int GetSomeResult()
{
// return some result here
}
}

Delegate interface implementation:

``` C#
public sealed class DelegatedImplementation : ISampleInterface
{
    private ISampleInterface _wrappedObj : ISampleInterface;

    public DelegatedImplementation(ISampleInterface wrappedObj)
    {
        _wrappedObj = wrappedObj;
    }
}

This would translate to:

``` C#
public sealed class DelegatedImplementation : ISampleInterface
{
private ISampleInterface _wrappedObj;

void ISampleInterface.DoSomeWork()
{
    _wrappedObj.DoSomeWork();
}

int ISampleInterface.GetSomeResult()
{
    return _wrappedObj.GetSomeResult();
}

public DelegatedImplementation(ISampleInterface wrappedObj)
{
    _wrappedObj = wrappedObj;
}

}

So, `ISampleInterface` implementation is transparently delegated to field `_wrappedObj`

``` C#
private ISampleInterface _wrappedObj : ISampleInterface;

Class, of course, can override this behavior:

``` C#
public sealed class DelegatedImplementation : ISampleInterface
{
private ISampleInterface _wrappedObj : ISampleInterface;

/* delegation for DoSomeWork member is ignored as class explicitly implement this member */
void ISampleInterface.DoSomeWork()
{        
    // do some other work
}

public DelegatedImplementation(ISampleInterface wrappedObj)
{
    _wrappedObj = wrappedObj;
}

}

This would translate to:

``` C#
public sealed class DelegatedImplementation : ISampleInterface
{
    private ISampleInterface _wrappedObj;

    /* delegation for DoSomeWork member is ignored as class explicitly implement this member */
    void ISampleInterface.DoSomeWork()
    {
        // do some other work
    }

    /* this member is still delegated as class doesn't override delegation behavior */
    int ISampleInterface.GetSomeResult()
    {
        return _wrappedObj.GetSomeResult();
    }

    public DelegatedImplementation(ISampleInterface wrappedObj)
    {
        _wrappedObj = wrappedObj;
    }
}
Area-Language Design

Most helpful comment

@HaloFour you can implement anything via source generators.
To avoid usage of source generators language features are introduced.

All 29 comments

This could be implemented via source generators without requiring additional modifications to the language.

@HaloFour you can implement anything via source generators.
To avoid usage of source generators language features are introduced.

@Opiumtm

you can implement anything via source generators.

To a degree, yes. However, source generators would work particularly well as there are no new grammatic structures that need to be added, just boilerplate code.

To avoid usage of source generators language features are introduced.

Language features are expensive in terms of implementation and support. There's no reason modify the language itself in order to handle something that can be easily handled via an outside tool, such as source generators. Source generators would also give you flexibility in how these things are implemented and promote a variety of solutions, whereas a language feature would lock you into whatever the language design team deemed the appropriate solution. Not to mention, source generators are actually likely to happen in the relatively short term, whereas a feature like this is likely going to take a backseat to the long list of things that the team has expressed interest in actually doing, assuming it would be considered at all.

@HaloFour no, you are wrong
Interface delegation is a quite common task and Borland Delphi (aka Object Pascal) language have this feature.
Code generator is unreliable and clumsy solution. It require additional build step or manual run to make source up-to-date.

@Opiumtm

Code is unreliable and clumsy solution. It require additional build step and manual run to make source code up-to-date.

I'm referring specifically to the proposed Roslyn compiler feature Source Generators which seeks to add source generation directly into the compiler pipeline. There would be no additional build step nor manual tasks. All you would do is add the generator reference to the project, just like you might with an analyzer today.

@HaloFour if you use custom source generator, it would be anyway clumsy to use especially if you publish your code as open-source. It would be very confusing for other developers who fork or just examine your lib to discover that your source code is generated automatically by tool. At first glance it would look like invalid code and maintainability would suffer. Source code generators are good for custom proxy generators, for serialization and so on. But use generator for regular development tasks is a bad idea.

@Opiumtm,

It is reasonable to assume that custom source generator would be packaged via nuget, thus their would be no confusion for devs forking your code as that nuget package would be pulled down and run automatically. This process works already for custom analyzers.

@DavidArno not for regular development tasks like interface delegation.

@Opiumtm

not for regular development tasks like interface delegation.

Annotation-driven delegation is exactly how Groovy handles it.

Anyway, you're free to disagree. But source generators are actually going to happen. Check out #124.

@HaloFour anyway interface delegation is a very simple language feature (it is very simple to understand and to implement and it only adds optional : ISomeInterface to field/property declaration - see code above), which was very useful in Object Pascal. This task in C# now require painful boilerplate code. Source code generators isn't a tool to implement this feature which was already introduced in many languages including ancient Object Pascal.

@Opiumtm

This task in C# now require painful boilerplate code.

Indeed, and the entire point of source generators is to produce painful boilerplate code.

already introduced in many languages including ancient Object Pascal.

The age of the language isn't relevant. C# is not Pascal, nor will it ever become Pascal. That's saying a bit considering that the person who designed much of Object Pascal also designed C#.

If you want Pascal on .NET, I suggest Oxygene.

@Opiumtm
This syntax in particular looks quite alien to C#:

private ISampleInterface _wrappedObj : ISampleInterface;

Maybe you can take some inspiration from Kotlin rather than Delphi and propose a new syntax?
https://kotlinlang.org/docs/reference/delegation.html

I personally never used the pattern and don't see much benefit in baking it into language. Can you give a real-world example of a project where delegation would be used a lot?

This syntax in particular looks quite alien to C#:

Not so alien. It is similar to how interface implementation is declared in classes.

``` C#
private ISampleInterface _wrappedObj : ISampleInterface;

// it is very similar to
public class SomeClass : ISampleInterface
{
}
```

I personally never used the pattern and don't see much benefit in baking it into language. Can you give a real-world example of a project where delegation would be used a lot?

One example is a unit tests. Other example is when you want to implement some standard interface (like IEnumerable or IDictionary) on your class if you don't want to expose internal collection or dictionary as a read-only property. Most simple way to do it is to delegate interface implementation to internal collection.

@dsaf

I personally never used the pattern and don't see much benefit in baking it into language. Can you give a real-world example of a project where delegation would be used a lot?

I'd never heard of the delegation pattern, but reading your Kotlin link, I realise it's a pattern I use; I just didn't know its name.

I use it in situations where I eg want a Dictionary, with slightly different behaviour and I don't want to use inheritance. The downside is that many .NET interfaces have horrific numbers of methods and properties, resulting in lots of biolerplate code that just calls the underlying type.

The source generators support in a future C# release will help hugely with this, as a simpler generator could take a delegate class and populate all that boilerplate for me. This would be another nail in the inheritance coffin. However, like you and @HaloFour, I do not see this as something that would need baking into the language, when it would work just fine with source generators.

@Opiumtm I meant it starts looking a bit like Scala/Swift/TypeScript in a confusing way:

def _wrappedObj : ISampleInterface;

I know it's a different language, but the pattern of putting the type on right side is too powerful to "spend" it on such a small feature as delegation. Developers are getting more polyglot these days.

@DavidArno yes, I realised this as well after @Opiumtm's comment.

@dsaf
It may be expressed lambda-like way

private ISampleInterface _wrappedObj => ISampleInterface;

@DavidArno as I have some Borland Delphi background (before moving to C# in 2008 I have developed apps on Delphi), interface delegation language feature is well-known to me and it's indeed very useful and conceptually simple to understand and implement on language level.

Isn't the requested feature just a mixin in disguise?

@vladd,

I'd say no. A mixin is just a form of multiple inheritance, whereas a delegate is a wrapper on top of another type. Source generators will make it possible to add mixins to C# as well. I'm not sure that's such a good idea though.

I need this! Why is it abandoned?

I'd love to see this feature implemented. The only thing I would reconsider is the suggested syntax. I really like the syntax shown in the link provided by dsaf In one of the earlier comments:

Maybe you can take some inspiration from Kotlin rather than Delphi and propose a new syntax?
https://kotlinlang.org/docs/reference/delegation.html

This is how the given example would look using that syntax:

//Delegate interface implementation:

public sealed class DelegatedImplementation : ISampleInterface by _wrappedObj
{
    private ISampleInterface _wrappedObj;

    public DelegatedImplementation(ISampleInterface wrappedObj)
    {
        _wrappedObj = wrappedObj;
    }
}

We are now taking language feature discussion on https://github.com/dotnet/csharplang for C# specific issues, https://github.com/dotnet/vblang for VB-specific features, and https://github.com/dotnet/csharplang for features that affect both languages.

The default interface implementation proposal (aka "traits") would support this use case. I will leave it as an exercise to the reader how, for now.

Another option to achieve (something similar to) this would be in the IDE - Visual Studio can already help you implement an interface on your class. Visual Studio could be extended to give you the option of implementing the interface by delegating to a field that implements the interface. You would still have the boilerplate interface delegation code, but at least you wouldn't have to write it yourself.

EDIT: As of November 1, 2017, it appears that Visual Studio 2015 now has this capability. Must have been added during an update? Or else I just never noticed before.

@bbi-yggy > Resharper can do that: Generate -> Delegating Members...

It's still code you need to maintain though as you mention.

@jeme,

Do you have a link to how that feature works, please?

Resharper can do that: Generate -> Delegating Members...

As can Roslyn (i added it when we first did Roslyn 'implement interface'):

image

Kotlin has that feature, and it would be greate if c# also had

The default interface implementation proposal (aka "traits") would support this use case. I will leave it as an exercise to the reader how, for now.

Respectfully, that proposal completely misses the mark on the value of interface delegation. The point of interface delegation is to move towards ‘prefer composition over inheritance’. Default implementations force coupling/reliance on those concrete implementations, whereas interface delegation stays decoupled from any implementation and only cares about the interface itself (and not in the interfaces that contain implementations sense of the word).

With default implementations you’re essentially creating a pseudo inheritance structure for interfaces and moving the opposite direction of ‘composition over inheritance’

Was this page helpful?
0 / 5 - 0 ratings