Champion: using aliases for any types (VS 17.6, .NET 8) · dotnet/csharplang · Discussion #8644 (original) (raw)
@CyrusNajmabadi Anonymous types have no user expressible name/syntax that you can use to refer to their type.
...which is kinda silly now in 2022: It's a shame that Anonymous Types were originally C# 2.0 to simplify code and allow C# users to be more expressive without the ceremony of fleshed-out class
type definitions, but because the design of ATs (and their atrocious ergonomics) seemingly haven't been touched since 2005 it means that any time-savings of using ATs are eliminated by the fact you need to revert back to using ceremonious class
definitions if you want to do anything useful with ATs:
- Implement an
interface
. - Participate in inheritance or composition.
- Target them for compile-time modification (Fody, etc).
- Return them from a function.
- Allow mutable properties.
I do understand why there were these restrictions back in C# 2.0: insufficient dev-time budget for release-day improvements, "less is more", and wanting to see how C# programmers would end-up using anonymous-types to see if further investment in improvements is worthwhile.
While it's now clear that C# 4.0's:Tuple<>
, and C# 7.0's ValueTuple<>
and C# 9.0's record class
types are all simply better at being low-ceremony types compared to ATs, unfortunately there's plenty of showstoppers:
I made a quick comparison table of the main different kinds of product-type/record/tuple in C# today, which I think demonstrates the gap that ATs fall into:
C# Version | Definition tedium | Has "real" named properties | EF/Core Linq projection | Exportable | Implement interface | Custom ctors and class invariants | |
---|---|---|---|---|---|---|---|
Mutable class | 1.0 | Medium | Yes | Yes | Yes | Yes | Yes, but not with EF: must use mutable properties in projections. |
Immutable class | 1.0 | High | Yes | Not supported | Yes | Yes | Yes, but not supported by EF at all. |
Anonymous Type | 2.0 | Low | Yes | Yes | No | No | No |
System.Tuple<> | 4.0 | Low | No | Not supported | Yes | No | No |
System.ValueTuple<> | 7.0 | Low | No. Member names set with attributes. Unsafe. | Not supported | Yes | No | No |
record class | 8.0 | Low | Yes, but no camelCasing of param names | Not supported | Yes | Yes | Yes, but this is surprisingly difficult, and often requires long-form ctor definitions, which eliminates one of record types' main ergonomic advantages: terseness. |
- Footnotes:
- EF Core requires ATs or POCOs for projections to non-entity types: it explicitly does not support record class types for reasons that make no sense to me (why can't they use
Object.ReferenceEquals
and just disregardoverride Object.Equals
?) - I make it a point to remind people that constructors in OOP are exactly how you implement class invariants.
* (For background): Invariants (preconditions, postconditions, etc) allow you to make hard guarantees about object-state. That is, ifclass Foo
has a ctor that enforces invariants (e.g. by throwing during construction if there's a parameter validation error) then functions with aFoo
-typed parameter can safely depend on those invariants - and this doesn't require full immutability either - overall it makes for much easier-to-understand software with fewer bugs caused by human misunderstandings.
* ...so it's very frustrating to me that the C# language doesn't make it easy to define or use custom constructors: so much of C# and its ecosystem doesn't seem to have a problem with default-constructors that leavenew
object instances in an invalid-state which must be manually late-initialized somehow (I'm looking at you WinForms and WPF, thenew()
generic constraint, Entity Framework, and others), evenrecord
types in C# 9.0 are surprisingly difficult to add constructor parameter validation to, despite obvious massive utility.
- EF Core requires ATs or POCOs for projections to non-entity types: it explicitly does not support record class types for reasons that make no sense to me (why can't they use
So looking at the table above, every product-type-kind in C# has its own deal-killers: some of which are unavoidable (e.g. the lack of safety with ValueTuple
if you get careless with member names), but some current deal-killers, as listed above, certainly (in my opinion) can and should be rectified somehow - so without getting too side-tracked from this thread's original topic of using
aliases, I'd like to suggest:
- Biggest positive impact: Force the EF Core team to support
record
types in query projections (and ideallyValueTuple
types too).- This will eliminate probably the single main use-case for ATs today, paving the way for the feature to be deprecated or even removed in future, as ATs should be entirely redundant given we now have
record
types andValueTuple
.
- This will eliminate probably the single main use-case for ATs today, paving the way for the feature to be deprecated or even removed in future, as ATs should be entirely redundant given we now have
- If the EF Core team remains intransigent then for the next biggest positive impact: make ATs optionally named types and participate in
partial class
definitions, which in-turn means we can finally make ATs exportable (i.e.public
orprotected
) and have ATs implement interfaces and participate in inheritance and composition.- ...and making ATs optionally named is what @NetMage's reply is suggesting - and while I support the idea, I'm not yet sold on the
using x = new { ... }
syntax. - Of course, doing this will just mean that ATs will become just a worse
record
-type, with zero advantages besides exclusive EF support - but would at least represent a forward step towards hopefully eventually removing ATs?
- ...and making ATs optionally named is what @NetMage's reply is suggesting - and while I support the idea, I'm not yet sold on the