Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Champion "permit methods in enum declarations" #297

Closed
6 tasks
gafter opened this issue Mar 20, 2017 · 24 comments
Closed
6 tasks

Champion "permit methods in enum declarations" #297

gafter opened this issue Mar 20, 2017 · 24 comments
Assignees
Milestone

Comments

@gafter
Copy link
Member

gafter commented Mar 20, 2017

  • Proposal added
  • Discussed in LDM
  • Decision in LDM
  • Finalized (done, rejected, inactive)
  • Spec'ed

See also dotnet/roslyn#3704

Reminder (since we don't yet have a work items list for this):

  • Let's review all the existing attributes that are allowed on class or struct contexts, but not enum. Maybe they should be updated to allow enum too. For example, SkipLocalsInitAttribute.
@gafter gafter added this to the X.0 candidate milestone Mar 20, 2017
@gafter gafter self-assigned this Mar 20, 2017
@jnm2
Copy link
Contributor

jnm2 commented Mar 20, 2017

This would allow me to get rid of so much {EnumName}Extensions clutter.

@alrz
Copy link
Member

alrz commented Mar 21, 2017

One thing that this would probably enable is overriding ToString for enums.

@MI3Guy
Copy link

MI3Guy commented Mar 21, 2017

My understanding is that this is not allowed by the CLR spec (I.8.5.2). Are there plans to change that?

The following restrictions are listed:

  • It shall have exactly one instance field, and the type of that field defines the underlying type of the enumeration.
  • It shall not have any methods of its own.
  • It shall derive from System.Enum (see Partition IV Library – Kernel Profile).
  • It shall not implement any interfaces of its own.
  • It shall not have any properties or events of its own.
  • It shall not have any static fields unless they are literal.

I imagine that removing all the restictions except for the single field and System.Enum requirements should be possible. (Although having events would be kind of useless because of the single field restriction.)

@gafter
Copy link
Member Author

gafter commented Mar 21, 2017

@MI3Guy That is a good point. We'll consider this when next we have the opportunity to make CLR changes.

@Thaina
Copy link

Thaina commented Mar 22, 2017

I prefer writing extension method

But well, I think what the proposer have explain. We don't need CLR changed. He just propose that we can just transpile enum method into extension method

@temporaryfile
Copy link

This feature won't truly shine until C# also supports enum inheritance and enum generic constraints. I run into enum limitations all the time in interop.

@Richiban
Copy link

I think this is probably a feature best left for ADTs.

Sure, methods could be added to enums but once C# has types such as discriminated unions (which can have methods) I think the use of raw enums will decrease dramatically.

@AustinBryan
Copy link

This is something that is very much needed. I don't like having to keep it all as extension methods; it's counter intuitive and requires more typing,

@Joe4evr
Copy link
Contributor

Joe4evr commented Jul 28, 2018

Maybe not entirely related, but probably worth discussing in this context: Is it possible to start emitting enums as a readonly type?

Admittedly, that may be a bit much. Currently, the value__ field isn't initonly, and I don't know if making that change would break assumptions elsewhere. But I think they should definitely get as close to readonly behavior as possible. Wouldn't it then be best to implicitly treat all enum-methods as a (yet only proposed) "readonly" method?

@yaakov-h
Copy link
Member

I believe the compiler already treats enums as readonly.

@TahirAhmadov
Copy link

If we allowed overriding ToString as an exception to the "no methods" rule, and just transpiled other properties/methods into extensions (at this point, probably using the new #5497 extension feature), it should be simple to accomplish. Right?

@jnm2
Copy link
Contributor

jnm2 commented Apr 7, 2022

Relaxing the CLR spec (or generally making changes to the CLR in tandem with C# releases) is something that has been happening. The appetite to do this is back now that the only people affected by CLR changes (in .NET Core/5+) are people who overrode defaults and asked to be affected.

@TahirAhmadov
Copy link

@jnm2 hopefully allowing to override ToString only is easy enough. The only reason I can think of to make other methods/properties as true "instance" members would be to allow virtual members, but I'm not sure that makes sense for enums anyway.

Regarding syntax, we need to decide where enum options stop and other members start. I propose semicolon:

enum Color
{
  Red,
  Green,
  Blue;
  public override string ToString()=>"asd";
  public void Foo() { ... }
  public int Prop => ((int)this) + 1; 
  // ...
}

@jnm2
Copy link
Contributor

jnm2 commented Apr 7, 2022

@TahirAhmadov Another reason not to emit extension methods is that the C# compiler would be generating a public class name.

@TahirAhmadov
Copy link

TahirAhmadov commented Apr 7, 2022

Another reason not to emit extension methods is that the C# compiler would be generating a public class name.

I actually think it's a benefit.
For example, if #5497 is released first with C# version M, and enum methods later with C# version N, then creating extensions allows those who are on C# M to consume libraries created on C# N and simply use the functionality - which to them looks like the same extension which is already supported in C# M.
Similarly, assuming extension can be implemented similarly to current "extension methods" (meaning, emitted as special static classes with special attributes), then somebody on C# 10 can consume libraries created using C# N, and they will just see static classes with the required functionality. Yes, using it is a bit awkward - they would have to do something like ColorExtensions123.Foo(Color.Red), but that's a minor stylistic difference only; they can still get their jobs done.
I know, it's tempting to say in a couple of years, "hey why are you still on C# 10", but realistically, in many companies management is wary of frequent upgrades and view them as risk. I disagree with that mantra personally; but in the real world, it's prevalent enough that I think we shouldn't shut those teams out completely.

@TahirAhmadov
Copy link

Another thought I had: together with methods, enums should allow declaring static fields; then scenarios like below become possible:

enum Foo
{
  A,
  B;
  static readonly string[] _displayNames = new[] { "Alpha", "Beta" };
  public string DisplayName => _displayNames[(int)this];
}

@ds5678
Copy link

ds5678 commented Sep 26, 2022

I found an Oracle patent for object-oriented enums mentioned in other discussions. Does that mean this is legally blocked until the patent's expiration date (2024-02-01)?

@HaloFour
Copy link
Contributor

@ds5678

That patent covers an implementation of enums as instances of a reference type with a somewhat specific shape. IANAL, I don't think allowing arbitrary methods within an enum declaration would fall afoul of that patent

@ds5678
Copy link

ds5678 commented Sep 27, 2022

I don't think allowing arbitrary methods within an enum declaration would fall afoul of that patent

I hope you're right. I'd love to get rid of my numerous {EnumName}Extensions classes. They've always felt like a workaround.

@ds5678
Copy link

ds5678 commented Oct 3, 2022

If this is implemented, will it be a runtime-dependent feature, i.e. only available on .NET 8+? Yes

@Rekkonnect
Copy link
Contributor

Just dropping a thought about this feature here, would it be nicer to force separating the other members of the enum into a different declaration? That would also require support for partial enums.

In each partial enum declaration, you can only have either normal members, or the enum's fields. That means, defining such an enum would look like this:

public partial enum S
{
    None,
    A,
    B,
    C,
}

partial enum S
{
    public bool IsValid => this is A or B;
    // would be nice to automatically access the max value as a compile-time constant
    // without having to define it manually everywhere
    // also for min value and total count
    private const int MaxValue = (int)C + 1;

    // either directly use value__ or a friendlier identifier to access the internal value
    public S Increment() => (S)((this.value__ + 1) % this.MaxValue);
}

@jnm2
Copy link
Contributor

jnm2 commented Aug 26, 2023

Maybe along with a rule that only one enum part can contain implicitly numbered members.

@Rekkonnect
Copy link
Contributor

That touches the proposal about partial enums and the declaration of their fields, but definitely some rules must be enforced to ensure that field declaration doesn't go out of hand.

@stamminator
Copy link

One thing that this would probably enable is overriding ToString for enums.

This would eliminate the common mistake where the developer forgets to call an extension method during string interpolation, causing the enum's ToString method to get called under the hood.

enum EmployeeType { Manager, Staff }

static class EmployeeTypeExtensions
{
    static string GetTitle(this EmployeeType employeeType) => employeeType switch
    {
        EmployeeType.Manager => "Team Leader",
        EmployeeType.Staff => "Team Member"
    }
}

static string GetEmployeeTitle(Employee employee)
{
    string titleVerbiage = $"Employee title: {employee.EmployeeType}"; // oops, should've been employee.EmployeeType.GetTitle()
}

Have to be careful though. Override ToString and JSON deserialization might break.

@dotnet dotnet locked and limited conversation to collaborators Nov 19, 2024
@dotnet dotnet unlocked this conversation Jan 6, 2025
@dotnet dotnet locked and limited conversation to collaborators Jan 6, 2025
@333fred 333fred converted this issue into discussion #8987 Jan 6, 2025

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Projects
None yet
Development

No branches or pull requests