Enumに拡張メソッドを追加する方法


122

私はこの列挙コードを持っています:

enum Duration { Day, Week, Month };

この列挙型の拡張メソッドを追加できますか?


1
これ見たことある?stackoverflow.com/questions/2422113/...
badpanda


短い答え、はい。この特定のケースでは、の使用を検討する必要がありますTimeSpan
Jodrell

1
列挙型で拡張メソッドを使用すると、私は汚れた感じになります。必要なものをカプセル化するクラスを作成します。enumはできるだけシンプルにしてください。関連するロジックがさらに必要な場合は、日、週、月に加えて、拡張メソッドに含まれていた他のロジックを含むDurationクラスを作成します。
Jason Evans

2
フラググループの列挙型拡張メソッドが好きです。例えば句があればに私が好むDay.IsWorkday()超える(Day & Days.Workday) > 0Days.Workdayとして定義しますMonday | Tuesday ... | Friday。前者は私の意見ではより明確であり、後者は正確に実装されています。
Sebastian Werk

回答:


114

このサイトによると:

拡張メソッドは、チームの他の人々が実際に発見して使用する方法で、既存のクラスのメソッドを作成する方法を提供します。列挙型は他のようなクラスであることを考えると、次のように拡張できることはそれほど驚くべきことではありません。

enum Duration { Day, Week, Month };

static class DurationExtensions 
{
  public static DateTime From(this Duration duration, DateTime dateTime) 
  {
    switch (duration) 
    {
      case Day:   return dateTime.AddDays(1);
      case Week:  return dateTime.AddDays(7);
      case Month: return dateTime.AddMonths(1);
      default:    throw new ArgumentOutOfRangeException("duration");
    }
  }
}

列挙型は一般的に最良の選択ではないと思いますが、少なくともこれにより、一部のスイッチ/ if処理を集中化し、より良い何かができるようになるまで、それらを少し抽象化できます。値が範囲内であることも確認してください。

詳細について、Microsft MSDNをご覧ください


「列挙型は悪だ」というコメントは場違いですが、実際には根拠があると思います。列挙型は特定のコンテキストや動作に固定されるため、列挙型は問題が多用される可能性があります。
Ed Schwehm

1
:このコンパイルして実行するので吸うのC#のようなもので列挙Duration d = 0;
グラハム

16
Given that enums are classesいいえ、クラスではありません。
ウィンガーセンドン2017

1
曜日や拡張メソッドIsWeekday()、IsWeekendday()のように、列挙型に直接関係する場合にのみ、この種の拡張を使用します。ただし、クラスは動作をカプセル化することを目的としているため、カプセル化する動作が多数または複雑な場合は、クラスの方が適しています。限定的で基本的なものであれば、私は個人的に列挙型の拡張機能を見つけます。ほとんどの設計決定と同様に、選択肢間にはあいまいな境界があります(IMO)。拡張はネストされたクラスではなく、トップレベルの静的クラスでのみ実行できることに注意してください。列挙型がクラスの一部である場合は、クラスを作成する必要があります。
フリーテキスト

51

Enumのインスタンスではなく、Enumタイプに拡張メソッドを追加することもできます。

/// <summary> Enum Extension Methods </summary>
/// <typeparam name="T"> type of Enum </typeparam>
public class Enum<T> where T : struct, IConvertible
{
    public static int Count
    {
        get
        {
            if (!typeof(T).IsEnum)
                throw new ArgumentException("T must be an enumerated type");

            return Enum.GetNames(typeof(T)).Length;
        }
    }
}

上記の拡張メソッドを呼び出すには、次のようにします。

var result = Enum<Duration>.Count;

これは真の拡張メソッドではありません。Enum <>はSystem.Enumとは異なるタイプであるため、機能します。


クラスは、staticそのすべてのメソッドが拡張機能のように動作することを保証できますか?
Jatin Sanghvi、2018

15
将来の読者のために:の名前のあいまいさEnum<T>は少し混乱します。クラスをEnumUtils<T>呼び出すこともでき、メソッド呼び出しはに解決されEnumUtils<Duration>.Countます。
ナモシェク

46

もちろん、たとえば、値にDescriptionAttribueon を使用することもできenumます。

using System.ComponentModel.DataAnnotations;

public enum Duration 
{ 
    [Description("Eight hours")]
    Day,

    [Description("Five days")]
    Week,

    [Description("Twenty-one days")] 
    Month 
}

ここで、次のようなことができるようにしたいとします。

Duration duration = Duration.Week;
var description = duration.GetDescription(); // will return "Five days"

拡張メソッドGetDescription()は次のように書くことができます:

using System.ComponentModel;
using System.Reflection;

public static string GetDescription(this Enum value)
{
    FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
    if (fieldInfo == null) return null;
    var attribute = (DescriptionAttribute)fieldInfo.GetCustomAttribute(typeof(DescriptionAttribute));
    return attribute.Description;
}

DisplayAttribute Localized GetDescriptionを使用することを除いて、サンプルの実行内容とほぼ同じ拡張機能を作成しようとしていました。歓声
ジョージ

名前空間は単なるSystem.ComponentModelだと思いますが、これは素晴らしい代替手段です。
TomDestry

素晴らしい見方で、実装と拡張コードを見せてくれてありがとう。積み重ねる:実装では、次のように呼び出すこともできます。var description = Duration.Week.GetDescription();
スペンサーサリバン

30

すべての答えは素晴らしいですが、拡張メソッドを特定のタイプの列挙型に追加することについて話し合っています。

明示的なキャストではなく現在の値のintを返すなど、すべての列挙型にメソッドを追加したい場合はどうでしょうか?

public static class EnumExtensions
{
    public static int ToInt<T>(this T soure) where T : IConvertible//enum
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException("T must be an enumerated type");

        return (int) (IConvertible) soure;
    }

    //ShawnFeatherly funtion (above answer) but as extention method
    public static int Count<T>(this T soure) where T : IConvertible//enum
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException("T must be an enumerated type");

        return Enum.GetNames(typeof(T)).Length;
    }
}

背後にIConvertibleある秘訣は、その継承階層ですMDSNを参照してください

ShawnFeatherlyの回答に感謝


1
ベストアンサー!
Marco Alves 2017年

同上。拡張機能を直接呼び出しても機能しますが(例MyExtention.DoThing(myvalue))実際には列挙型に接続しません(例myvalue.DoThing()
Sinaesthetic

1
参考までに、C#7.3はEnumをジェネリック型の制約としてサポートするようになりました
redtetrahedron


5

MSDNを参照してください。

public static class Extensions
{
  public static string SomeMethod(this Duration enumValue)
  {
    //Do something here
    return enumValue.ToString("D"); 
  }
}

7
void列挙型の戻り値は奇妙なものです。より現実的なサンプルについて考えます。
psubsee2003 2013年

3
@ psubsee2003 OPは確かにこれを彼のニーズに合うように変更するのに十分な知識を持っていますか?なぜサンプルが重要なのか、最初の質問に答えるだけで十分です。
LukeHennerley 2013年

3
MSDNで奇妙なコード例を見つけたのは私だけですか?ほとんどの場合、彼らが何をしようとしているのかを理解するために実際の努力が必要です!
スタック

0

c#https://github.com/simonmau/enum_extの enum拡張を作成しました

これはタイプセーフの実装にすぎませんが、うまく機能するため、共有するパッケージを作成しました-楽しんでください

public sealed class Weekday : TypeSafeNameEnum<Weekday, int>
{
    public static readonly Weekday Monday = new Weekday(1, "--Monday--");
    public static readonly Weekday Tuesday = new Weekday(2, "--Tuesday--");
    public static readonly Weekday Wednesday = new Weekday(3, "--Wednesday--");
    ....

    private Weekday(int id, string name) : base(id, name)
    {
    }

    public string AppendName(string input)
    {
        return $"{Name} {input}";
    }
}

例が役に立たないことは知っていますが、あなたはアイデアを理解します;)


0

簡単な回避策。

public static class EnumExtensions
{
    public static int ToInt(this Enum payLoad) {

        return ( int ) ( IConvertible ) payLoad;

    }
}

int num = YourEnum.AItem.ToInt();
Console.WriteLine("num : ", num);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.