-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Comments
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. |
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. |
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. |
Thinking out loud here, it seems that I could break my proposal in two:
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:
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",
}
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 }); |
I duped this (as linked above), with an alternative syntax. |
+1 |
1 similar comment
+1 |
+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 records (#206) can implement interfaces. I think that satisfies the underlying need of this request. |
@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. |
+1 |
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:
|
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. |
+1 Also thinking out loud here. Concern 1What 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.
This would roughly compile to:
Concern 2What about default values? I might not want to provide an implementation for a method or omit a property. Possibly:
Why?Unit tests and mocking. For trivial scenarios (not involving abstract or sealed classes) you could mock your objects as follows:
|
It's not clear that you're initializing a property of type
|
@alrz +1. Just use a comma for consistency. Isn't the return type+visibility redundant (unless internal interfaces make an appearance)?
|
@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.. |
@Spacefish And well I know it. I was answering @jbtibor's question, "Are there local classes in C#? Or this is a future feature?" |
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. |
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. |
Are there public APIs in Java that have anonymous types? |
@RUSshy See the comment above: #13 (comment)
Also, this kind of issue now belongs to the csharplang repo, and there is a very similar request already opened there: dotnet/csharplang#1542. |
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. |
Just noticed this in Don Syme’s History of F# paper for HOPL 😁 |
very disappointed that this was closed without reason. |
Feature requests for C# can be opened in https://github.com/dotnet/csharplang |
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. |
There is: |
"We will never make a 32-bit operating system." |
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. |
Add tests for underlining reassignments
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. |
@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? |
This should be brought to C#! Give your votes here: |
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. |
Under what circumstances would that be better than |
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. |
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. |
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. |
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. |
@tmat has started a discussion on this: dotnet/csharplang#6049 |
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.
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:
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()
orobj.Baz()
), but the compiler needs to know this.This could be solved by either adding a new syntax to this implementation:
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.
The text was updated successfully, but these errors were encountered: