Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: Anonymous types that implement interfaces #13

Closed
nvivo opened this issue Jan 16, 2015 · 69 comments
Closed

Feature request: Anonymous types that implement interfaces #13

nvivo opened this issue Jan 16, 2015 · 69 comments

Comments

@nvivo
Copy link

nvivo commented Jan 16, 2015

It would be very useful if C# anonymous types could implement interfaces, including methods. This would make a huge difference when developing using interface driven design. It would also allow for very easy mocking in unit tests.

interface IFooBar {
    string Foo { get; }
    int Bar(string s);
}

void MethodA () {
    // explicitly typed
    var myFooBar = new IFooBar {
        Foo = "xyz",
        Bar = s => s.Length
    };

    MethodB(myFooBar);
}

IFooBar MethodB(IFooBar fooBar) {
    // implicit typed
    return new {
        Foo = "abc",
        Bar = fooBar.Bar
    };
}

In TypeScript and other dynamic languages, this has proven to be really useful and reduces a lot of boilerplate code.

It seems that implementing this in C# wouldn't break any rules as anonymous types are already classes internally, and the compiler could just make it implement the interface and use the same rules for checking type.

The only issue I can think right now is the method implementation. How to differ between a method and a property that is a delegate:

interafce IFoo {
    int Bar(int i);
    Func<int, int> Baz { get; }
}

void IFoo GetFoo() {
    return new {
        Bar = i => 1, // ?
        Baz = i => 2; // ?
    }
}

It seems that from the perspective of the C# consumer it wouldn't make much difference, as both can be called using the same syntax (obj.Bar() or obj.Baz() ), but the compiler needs to know this.

This could be solved by either adding a new syntax to this implementation:

void IFoo GetFoo() {
    return new {
        Bar(int i) => 1,   // method
        Baz = i => 2;  // deletage
    }
}

Or by just defaulting to methods unless the interface calls for a property. That would make the first example with the same code valid, and I guess would make the syntax better.

@theoy theoy added this to the Unknown milestone Jan 16, 2015
@nemec
Copy link

nemec commented Jan 16, 2015

I like the idea. Although anonymous types aren't meant to be passed outside the scope where they're created, interfaces are all about the contract so it doesn't matter much that the implementation is compiler-generated.

Per your suggestion about methods, it looks like C# 6 already blurs the line between methods and delegates (from a syntax standpoint), so maybe it's not such a far-fetched suggestion.

@mattwar
Copy link
Contributor

mattwar commented Jan 16, 2015

The reason anonymous types are not passable outside the scope where they are created is due to a lack of unification of types across assemblies in the runtime, not a design philosophy. Giving anonymous types an interface is a good idea. I believe it was proposed during the design for anonymous types but was outside the intent of the feature at the time.

@MadsTorgersen
Copy link
Contributor

This proposal seems related to object expressions in F# or anonymous inner classes in Java, both of which let you specify class implementations right when you new the objects up.

You could certainly imagine an evolution of anonymous objects in C# that would combine the current behavior with the ability to inherit classes, implement interfaces and supply member implementations.

@nvivo
Copy link
Author

nvivo commented Jan 17, 2015

Thinking out loud here, it seems that I could break my proposal in two:

  1. Allow anonymous objects to include methods and writeable properties.
  2. Allow anonymous objects to implement interfaces

Once the first is achieved, the second one should be straightforward.

To me, it is clear the idea is very useful and would reduce drastically the number "dumb" classes I need just to return models in interface driven code.

But it raises some questions:

  • How to declare a writeable property in an anonymous object?
  • If the property is writeable, does it require an initial value?
new {
    // new modifier keyword?
    readwrite Name = "foo",  
    readwrite string Name,

    // no types, always infer from the value?
    readwrite Name = default(string),

    // no value implies it will be set later, hence writeable? - looks strange
    string Name;

    // borrow syntax from real properties?
    string Name { get; set; },
    Name { get; set; } = "initial value",

    // or imply the get and require only the set?
    string Name { set; },
    Name { set; } = "initial value",
}
  • And If I know beforehand the declaration type, could it infer the interface?
interface IPerson {
    string Name { get; set; }
    bool IsDeceased { get; set; }
}

IPerson GetPerson() {
    return new {
        Name = "John"
    };
}

This would create some form of "duck typing", making like implementing one-time used interfaces easier.

interface ISystemClock {
    DateTimeOffset Now()
} 

iocContainer.Resolve<ISystemClock>().With(new { Now() => DateTimeOffset.Now });

@Porges
Copy link

Porges commented Jan 28, 2015

I duped this (as linked above), with an alternative syntax.

@iSynaptic
Copy link

+1

1 similar comment
@aluanhaddad
Copy link

+1

@dsaf
Copy link

dsaf commented Apr 1, 2015

+1

@nvivo Are you requesting specifically interface implementations or subtyping as well?

Depending on that #1728 might be a duplicate.

Also what do you mean by saying "interface driven design"?

Relevant quote:

http://www.jot.fm/issues/issue_2005_07/article1.pdf

Note that as generalizations, interfaces compete with abstract classes. In fact, in
practice interfaces and abstract classes are sometimes used as if they were the same
concept. However, since abstract classes have the potential to pass on implementation to
their subclasses, they should be used if (and only if) the relationship to the subclasses is
genetic, i.e., if it is (or at least could be) based on the inheritance of internal structure, that
is, implementation. If on the other hand the relationship is based on pure function (or,
weaker still, on sameness of protocol), interfaces should be used. For instance, a linked
list and a dynamic array would normally not be genetically related (i.e., have no common
pieces of implementation), yet they share the interface of lists (specifying sequential
access to their elements).

@gafter
Copy link
Member

gafter commented Apr 1, 2015

Note that records (#206) can implement interfaces. I think that satisfies the underlying need of this request.

@dsaf
Copy link

dsaf commented Apr 1, 2015

@gafter Can records be declared within method body? I think this request is about not polluting the namespaces and creating explicit named types, rather than creating immutable classes with compact syntax. I believe these are different.

@jbtibor
Copy link

jbtibor commented May 14, 2015

+1

@t00
Copy link

t00 commented Jul 24, 2015

As anonymous classes are created with only getters available and only in the current scope this feature might be non-trivial to implement.

But implementation can happen in 2 stages, 1 for getter-only interfaces and 2 for getter and setter + methods.

I would say implementing only interfaces containing property getters would be a huge improvement and should not change anonymous constructor code apart from adding inheritance to the anonymous class.

Inheritance syntax should be explicit and any setter or method on the interface or missing property on the anonymous implementation should trigger an error:

interface IPerson
{
    string Name { get; }
    bool IsDeceased { get; }
}

interface IEmployee
{
    string Boss { get; }
}

IPerson GetPerson()
{
    return new : IPerson, IEmployee
    {
        Name = "John",
        IsDeceased = true,
        Boss = "OK",
        ExtraProperty = 123
    };
}

@aluanhaddad
Copy link

I am skeptical of the property implementation syntax, I think it should be something like

return new : IPerson, IEmployee
{
    Name => "John",
    IsDeceased => true,
    Boss => "OK",
    ExtraProperty => 123
};

or

return new : IPerson, IEmployee
{
    Name { get; } = "John",
    IsDeceased { get; } = true,
    Boss { get; } = "OK",
    ExtraProperty { get; } = 123
};

because we are defining a type that implements the properties of the interfaces, where as in the case of anonymous types, the compiler is generating a type definition for us based on the names and values of the instantiation expression.

@jcdickinson
Copy link

+1

Also thinking out loud here.

Concern 1

What about methods? One thing I've done for mock objects (without a mocking framework) in the past is to store a delegate that has the matching signature in a private field and invoke that from the interface. E.g.

interface ICustomer {
  string Name { get; }
  string Surname { get; }
  bool IsValid();
}

return new ICustomer() {
  Name = "Jim",
  Surname = "Bob",
  IsValid = () => true
};

This would roughly compile to:

class ICustomerAnyonymousType {
  public string Name { get; set; }
  public string Surname { get; set; }
  public Func<bool> IsValidImplementation { get; set; } // Or a private field
  public bool IsValid() {
    var impl = IsValidImplementation;
    if (impl == null) throw new NotImplementedException(); 
    return impl(); 
  }
}

return new ICustomerAnyonymousType() {
  Name = "Jim",
  Surname = "Bob",
  IsValidImplementation = () => true
};

Concern 2

What about default values? I might not want to provide an implementation for a method or omit a property. Possibly:

return new default ICustomer() {
  Name = "Jim"
};

Surname would return null, and IsValidImplementation would be set to null (causing a NotImplementedException). If default is omitted all members must be provided (else there will be compile failure).

Why?

Unit tests and mocking. For trivial scenarios (not involving abstract or sealed classes) you could mock your objects as follows:

[Fact]
public void DisplayCustomer_ValidateCalled()
{
    var validateCalled = false;
    var customer = new ICustomer() {
      Name = "Jim",
      Surname = "Bob",
      IsValid = () => validateCalled = true
    }
    sut.Display(customer);
    Assert.True(validateCalled);
}

[Fact]
public void DeleteCustomer_Deleted()
{
    var customer = new default ICustomer() {
      Name = "Jim",
      Surname = "Bob"
      // Omit IsValid because it is never called.
      // NotImplementedException would be thrown if it was
      // somehow called.
    }
    sut.Deleted(customer);
    Assert.Null(sut.GetCustomer(customer));
}

@alrz
Copy link
Member

alrz commented Sep 30, 2015

@jcdickinson

return new ICustomer() {
  Name = "Jim",
  Surname = "Bob",
  IsValid = () => true
};

It's not clear that you're initializing a property of type Func<bool> or implementing the method. I think this should be done in a more explicit way like:

return new ICustomer {
  public string Name => "Jim";
  public string Surname => "Bob";
  public bool IsValid() => true;
};

@jcdickinson
Copy link

@alrz +1. Just use a comma for consistency. Isn't the return type+visibility redundant (unless internal interfaces make an appearance)?

return new ICustomer {
  Name = "Jim",
  Surname = "Bob",
  IsValid() => true,
  Foo = bar,
  Baz() => false
};

@jnm2
Copy link
Contributor

jnm2 commented Jan 19, 2018

@jbtibor dotnet/csharplang#130

@Spacefish
Copy link

@jnm2 that´s not really the same thing.. anonymous types implementing an interface would allow you to pass them as a reference to multiple callbacks to a long running action / task.. Such a pattern is heavily used in Android in Java..
There are typically multiple things that could happen if you trigger some asynchronous actions.. If you only have one callback/delegate you have to have multiple parameters which are not used in all cases, passing an object which implements functions for that cases looks much cleaner / is more readable.

@jnm2
Copy link
Contributor

jnm2 commented Jan 26, 2018

@Spacefish And well I know it. I was answering @jbtibor's question, "Are there local classes in C#? Or this is a future feature?"

@HaloFour
Copy link

HaloFour commented May 19, 2018

@gafter

I don’t know what you may be missing, but I believe that local class declarations give you the full power in a much more readable and clear form.

But with unnecessary boilerplate, even by Java standards. In my experience local type declarations are nearly non-existent in the Java ecosystem, but anonymous classes are exceptionally common. This was especially true leading up to Java finally getting lambdas. If you look at any of the helper packages for Java that deal in functional concepts or callbacks they all use anonymous types, including those from Google, Netflix, Apache, Amazon, etc., both internally and in any example they provide for using their APIs.

I could probably count the number of times I've seen local declarations in the wild on one hand, at best. The only times I've ever reached for them myself was to emulate closures in lambdas due to Java forcing all enclosed locals to be effectively final. With Java 10 that's no longer necessary and I will refactor all of that code to using anonymous classes instead.

public IDisposable Foo() {
    class Foo : IDisposable {
        public void Dispose() {
            Console.WriteLine("Disposed!");
        }
    };
    return new Foo();
}

vs.

public IDisposable Foo() => new IDisposable {
    void Dispose() => Console.WriteLine("Disposed!");
};

There's nothing unreadable about that second example. If anything I think it does a much better job of focusing on what it actually does.

@HaloFour
Copy link

@gafter

Furthermore, if Java/Android interop is given as one of the reasons for adopting default interface members (of which I am in favor, regardless of the justification), then I can state that the same interop is a good reason to adopt anonymous classes.

As @errorx666 stated, Android dev is interface-driven for callbacks. This makes them cumbersome to use from C# as you have to declare an entire separate class. Local types help, but that's still a lot of ceremony to declare and instantiate to effectively emulate what would be an exceptionally verbose lambda. To improve that situation from C# I would recommend that anonymous types are considered. Even better would be to allow lambda syntax with interfaces with exactly one required method (or also expand that out to abstract types, like Scala supports). Incidentally, poking at the Android docs, they clearly favor anonymous classes also.

@gafter
Copy link
Member

gafter commented May 19, 2018

that the same interop is a good reason to adopt anonymous classes.

Are there public APIs in Java that have anonymous types?

@HaloFour
Copy link

HaloFour commented May 19, 2018

@gafter

Are there public APIs in Java that have anonymous types?

No, there are public APIs throughout Java and the Java ecosystem that expose interfaces for use as callbacks, and the idiomatic (and natural) approach to implementing them is via anonymous types.

https://github.com/google/guava/wiki/FunctionalExplained
https://hc.apache.org/httpcomponents-asyncclient-ga/quickstart.html
https://commons.apache.org/proper/commons-functor/examples.html
https://github.com/Netflix/Hystrix/wiki/How-To-Use#Reactive-Execution
http://reactivex.io/documentation/operators/filter.html
https://developer.android.com/guide/topics/ui/ui-events
https://developer.android.com/guide/topics/location/strategies
https://docs.oracle.com/javafx/2/events/handlers.htm
https://docs.oracle.com/javase/7/docs/api/java/beans/EventHandler.html
http://netty.io/wiki/user-guide-for-4.x.html#wiki-h3-9
https://jersey.github.io/documentation/latest/rx-client.html
https://github.com/AsyncHttpClient/async-http-client#using-custom-asynchandlers

I honestly think you'd find it pretty difficult to find API docs that would suggest implementing callback interfaces using local types over anonymous types.

@svick
Copy link
Contributor

svick commented Jan 22, 2019

@RUSshy See the comment above: #13 (comment)

We have no expectation of ever doing anything like this.

Also, this kind of issue now belongs to the csharplang repo, and there is a very similar request already opened there: dotnet/csharplang#1542.

@HaloFour
Copy link

Similar but not quite. It might be worth opening a separate issue. I agree that it doesn't seem likely that it will happen given the tepid response from some team members, but I do think that they would be very useful especially when it comes to Android interop.

@Porges
Copy link

Porges commented Sep 5, 2019

Just noticed this in Don Syme’s History of F# paper for HOPL 😁

Surprisingly this feature is yet to make it into any version of C#.

@Spongman
Copy link

Spongman commented May 6, 2020

very disappointed that this was closed without reason.

@gafter
Copy link
Member

gafter commented May 7, 2020

Feature requests for C# can be opened in https://github.com/dotnet/csharplang

@Spongman
Copy link

Feature requests for C# can be opened in https://github.com/dotnet/csharplang

sounds like that would just be a waste of time:

We have no expectation of ever doing anything like this.

@CyrusNajmabadi
Copy link
Member

sounds like that would just be a waste of time:

You never know. Someone might champion it. If it is not opened, it certainly won't happen though.

@HaloFour
Copy link

There is:

dotnet/csharplang#2517
dotnet/csharplang#2298

@dan-lugg
Copy link

Feature requests for C# can be opened in https://github.com/dotnet/csharplang

sounds like that would just be a waste of time:

We have no expectation of ever doing anything like this.

"We will never make a 32-bit operating system."
Bill Gates (speaking at the launch of MSX in 1989).

@BrianBu01
Copy link

Is it time to revisit this now that Record/Data construct solves most of the mention concerns?

Maybe this idea wasn't right for class interfaces, but it is for Record/Data interfaces.

CyrusNajmabadi added a commit that referenced this issue May 25, 2021
Add tests for underlining reassignments
@rashadrivera
Copy link

rashadrivera commented Jun 30, 2021

t00 proposed:

interface IPerson
{
    string Name { get; }
    bool IsDeceased { get; }
}

interface IEmployee
{
    string Boss { get; }
}

IPerson GetPerson()
{
    return new : IPerson, IEmployee
    {
        Name = "John",
        IsDeceased = true,
        Boss = "OK",
        ExtraProperty = 123
    };
}

Sorry, but the syntax just seem wrong for the C# language. It's to laxed like JavaScript is and takes away from the strong-typing we've come to expect. While I believe C# could benefit from adding scaffolding to anonymous types (albeit via interfaces or other), I don't think this proposed code example is the way to go about it.

@Spongman
Copy link

Spongman commented Jul 1, 2021

@rashadrivera there's no weak-typing used in that example. the example uses syntax similar to Object Initializers introduced in C# 3.0. Have you updated to C# 3.0 yet?

@FANMixco
Copy link

This should be brought to C#!

Give your votes here:
dotnet/csharplang#4301

@vbjay
Copy link

vbjay commented Aug 8, 2022

Being able to do something like

using (var itm = new {Name = "Jay"})
{
	//Do stuff with itm knowing itm.Name would be disposed and any other IDIsposable properties in itm.  
}

would be useful.

@HaloFour
Copy link

HaloFour commented Aug 8, 2022

@vbjay

Being able to do something like ... would be useful.

Under what circumstances would that be better than using on the disposables themselves? Even with the original proposal here there's nothing to suggest that the implemented interface would be automatically delegated to any, let alone all, of the members of the anonymous type.

@vbjay
Copy link

vbjay commented Aug 8, 2022

Well in one place is in Observable.Using call. Create an anonymous object wrapping different items to use as resources and in the Observable factory part can use the resource object items.

@vbjay
Copy link

vbjay commented Aug 8, 2022

I understand I can create a class and write the code but having anonymous objects able to implement IDisposable and have it create disposable implimentation that disposes items that are disposable would be useful.

I get the problem of creating an anonymous object with a member pointing to something IDisposable that exists outside of anonymous object and then that object is disposed because the anonymous object was disposed.

@vbjay
Copy link

vbjay commented Aug 8, 2022

Maybe like key keyword in vb when creating a comparable anonymous object, a new keyword could be out in front of members we want disposed. If that keyword is used at all in that anonymous creation then it is IDisposable and items marked by keyword get added to items that are disposed.

var x = new { Dispose SomeIDisposable x = Blaaa}

Something like that.

@HaloFour
Copy link

HaloFour commented Aug 8, 2022

@vbjay

I'd suggest opening a new discussion on the csharplang repository if that's something you want to suggest. This particular issue is already closed and this repository isn't used to propose new language features anymore.

@jnm2
Copy link
Contributor

jnm2 commented Aug 9, 2022

@tmat has started a discussion on this: dotnet/csharplang#6049

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests