Home Php C# Sql C C++ Javascript Python Java Go Android Git Linux Asp.net Django .net Node.js Ios Xcode Cocoa Iphone Mysql Tomcat Mongodb Bash Objective-c Scala Visual-studio Apache Elasticsearch Jar Eclipse Jquery Ruby-on-rails Ruby Rubygems Android-studio Spring Lua Sqlite Emacs Ubuntu Perl Docker Swift Amazon-web-services Svn Html Ajax Xml Java-ee Maven Intellij-idea Rvm Macos Unix Css Ipad Postgresql Css3 Json Windows-server Vue.js Typescript Oracle Hibernate Internet-explorer Github Tensorflow Laravel Symfony Redis Html5 Google-app-engine Nginx Firefox Sqlalchemy Lucene Erlang Flask Vim Solr Webview Facebook Zend-framework Virtualenv Nosql Ide Twitter Safari Flutter Bundle Phonegap Centos Sphinx Actionscript Tornado Register | Login | Edit Tags | New Questions | 繁体 | 简体


10 questions online user: 4

0
votes
answers
30 views
+10

創建一個std :: vector的枚舉

2

我想要使用枚舉std::vector<bool>,因爲我想將某些值表示爲一個位向量。創建一個std :: vector的枚舉

所以,我想下面的代碼:

enum class ProximityStateEnum : std::vector<bool> { 

    Unknown = std::vector<bool>{false,false}, 

    NotConnected = std::vector<bool>{false,true}, 

    Connected = std::vector<bool>{true,false}, 

    ConnectedButNotLatched = std::vector<bool>{true,true} 

}; 

但是,當我與此編譯代碼,我得到的錯誤underlying type ‘std::vector<bool>’ of ‘ProximityStateEnum’ must be an integral type。我該如何創建一個向量枚舉?

沙发
0
4

你不能; enum s只能基於整型。

如果它可以是有幫助的,因爲C++ 14可以使用二進制文字指定它們的值:

enum class ProximityStateEnum { 
    Unknown = 0b00, 
    NotConnected = 0b01, 
    Connected = 0b10, 
    ConnectedButNotLatched = 0b11 
}; 

這是相當多的你的想法是明確的,但方式更加緊湊和數量級比任何假設的基於std::vector的解決方案更高效。

,往往是在enum S其中特定位具有特定的含義使用另一種可能性是用表情來構建它們:

enum ProximityStateEnum { 
    Unknown = 0b00, 
    NotConnected = 0b01, 
    Connected = 0b10, 
    ConnectedButNotLatched = Connected | NotConnected 
}; 
+0

感謝您的回答。在我的情況下,我真的需要這種類型是'std :: vector '。我可能會看看結構與靜態成員。 – Xatyrian

+1

@Xatyrian:聽起來像一個XY問題,你應該描述爲什麼你需要它是一個'std :: vector '。 –

+1

此外,只有靜態成員的'struct'沒有多大意義,請使用名稱空間。 –

板凳
0
3

enumenum class並不意味着作爲別名任意對象的實例 - 他們限於整數類型,並且每個顯式值必須是的常數表達式。有關更多信息,請參閱cppreference page on enum

如果你想給一個名稱的std::vector<bool>特定的情況下,無論是使用功能或inline變量:

auto Unknown()    { return std::vector<bool>{false,false}; } 
auto NotConnected()   { return std::vector<bool>{false,true}; } 
auto Connected()    { return std::vector<bool>{true,false}; } 
auto ConnectedButNotLatched() { return std::vector<bool>{true,true}; } 

// ...or, in C++17: 
inline auto Unknown =    std::vector<bool>{false,false}; 
inline auto NotConnected =   std::vector<bool>{false,true}; 
inline auto Connected =    std::vector<bool>{true,false}; 
inline auto ConnectedButNotLatched = std::vector<bool>{true,true}; 

在您的特定用途的情況下,你並不真的需要一個std::vector因爲你知道大小提前確定您的bitset的 - std::bitset二進制文字將工作做好:

enum class ProximityStateEnum : /* some suitable type */ 
{ 
    Unknown =    0b00, 
    NotConnected =   0b01, 
    Connected =    0b10, 
    ConnectedButNotLatched = 0b11 
}; 
+0

感謝您的回答。事實上,'std :: bitset'也可以使用,但我之前決定堅持使用矢量,所以現在我不能真正改變。沒有枚舉的你的方法很有趣,但是因爲我需要將不同的值分組,所以我想讓它們成爲結構體的靜態成員 – Xatyrian

地板
0
0

至於說其他,枚舉只能是整體類型。

如果bool向量的長度恆定(例如,其中是2),則可以使用std::array<bool, length>替代std::vector<bool>。使用std::array而不是std::vector的一個優點是您可以將其定義爲constexpr

因此,一個合理的(IMHO)另一種方法是定義基於std::size_t一個枚舉類型,與來自0開始順序值,

enum ProximityStateEnum : std::size_t 
{ 
    Unknown = 0U, 
    NotConnected, 
    Connected, 
    ConnectedButNotLatched 
}; 

std::arrays一個staticconstexprstd::arraybool

// .......................... last value of the enum vvvvvvvvvvvvvvvvvvvvvv 
static constexpr std::array<std::array<bool, 2U>, 1U+ConnectedButNotLatched> 
    pseArr { { { {false, false} }, { {false, true} }, 
       { {true, false} }, { {true, true} } } }; 

你可以使用如下

pseArr[NotConnected][0] 
1763
votes
answers
15 views
+10

創建將T限制為枚舉的通用方法

我正在構建一個擴展Enum.Parse概念的函數

  • 允許在未找到枚舉值的情況下解析默認值
  • 不區分大小寫

所以我寫了以下內容:

public static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
{
    if (string.IsNullOrEmpty(value)) return defaultValue;
    foreach (T item in Enum.GetValues(typeof(T)))
    {
        if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
    }
    return defaultValue;
}

我得到一個Error Constraint不能是特殊類System.Enum

很公平,但是有一個解決方法允許Generic Enum,或者我將不得不模仿該Parse函數並將類型作為屬性傳遞,這會迫使您的代碼出現醜陋的拳擊要求。

編輯以下所有建議都非常感謝,謝謝。

已經解決了(我已離開循環以保持不區分大小寫 - 我在解析XML時使用它)

public static class EnumUtils
{
    public static T ParseEnum<T>(string value, T defaultValue) where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");
        if (string.IsNullOrEmpty(value)) return defaultValue;

        foreach (T item in Enum.GetValues(typeof(T)))
        {
            if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        }
        return defaultValue;
    }
}

編輯:(2015年2月16日)Julien Lebosquain最近在MSIL或F#下面發布了一個編譯器強制類型安全通用解決方案,非常值得一看,並且是一個upvote。如果解決方案在頁面上向上冒泡,我將刪除此編輯。

up vote 935 down vote accepted favorite
沙发
+9350
+50

由於EnumType實現了IConvertible接口,更好的實現應該是這樣的:

public T GetEnumFromString<T>(string value) where T : struct, IConvertible
{
   if (!typeof(T).IsEnum) 
   {
      throw new ArgumentException("T must be an enumerated type");
   }

   //...
}

這仍然允許傳遞實現的值類型IConvertible但機會很少。

從.NET 2.0開始,泛型可用。因此它們也可以在vb 2005中使用。 - Vivek 2009年6月1日17:07

那麼,如果你選擇沿著這條路走下去,那就讓它更受限制......使用“類TestClass 其中T:struct,IComparable,IFormattable,IConvertible“ - Ricardo Nolde 2010年9月27日18:42

另一個建議是使用標識符TEnum定義泛型類型。因此:public TEnum GetEnumFromString(字符串值)其中TEnum:struct,IConvertible,IComparible,IFormattable {} - Lisa 11年11月24日在6:52

通過包含其他接口不會獲得太多收益,因為幾乎所有內置值類型都實現了所有這些接口。對於通用擴展方法的約束尤其如此,這對於在枚舉上操作非常方便,除了這些擴展方法就像感染所有對象的病毒一樣。IConvertable至少可以縮小它的範圍。 - russbishop 2014年3月5日18:04

@SamIam:當你發佈時,這個帖子就是那個,6歲半,你是對的,沒有編譯時檢查任何答案。然後僅僅3天之後,經過6年,你實現了自己的願望 - 請看Julien Lebosquain在下面的帖子。 - 大衛I.麥金托什2015年4月12日1:55

+5380

C#7.3最終支持此功能!

以下代碼段(來自dotnet示例)演示瞭如何:

public static Dictionary<int, string> EnumNamedValues<T>() where T : System.Enum
{
    var result = new Dictionary<int, string>();
    var values = Enum.GetValues(typeof(T));

    foreach (int item in values)
        result.Add(item, Enum.GetName(typeof(T), item));
    return result;
}

請務必在C#項目中將語言版本設置為7.3版。


原始答案如下:

我已經遲到了,但我把它視為一個挑戰,看看它是如何完成的。它不可能在C#(或VB.NET中,但向下滾動F#),但在MSIL中是可能的。我寫了這個小東西

// license: http://www.apache.org/licenses/LICENSE-2.0.html
.assembly MyThing{}
.class public abstract sealed MyThing.Thing
       extends [mscorlib]System.Object
{
  .method public static !!T  GetEnumFromString<valuetype .ctor ([mscorlib]System.Enum) T>(string strValue,
                                                                                          !!T defaultValue) cil managed
  {
    .maxstack  2
    .locals init ([0] !!T temp,
                  [1] !!T return_value,
                  [2] class [mscorlib]System.Collections.IEnumerator enumerator,
                  [3] class [mscorlib]System.IDisposable disposer)
    // if(string.IsNullOrEmpty(strValue)) return defaultValue;
    ldarg strValue
    call bool [mscorlib]System.String::IsNullOrEmpty(string)
    brfalse.s HASVALUE
    br RETURNDEF         // return default it empty

    // foreach (T item in Enum.GetValues(typeof(T)))
  HASVALUE:
    // Enum.GetValues.GetEnumerator()
    ldtoken !!T
    call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    call class [mscorlib]System.Array [mscorlib]System.Enum::GetValues(class [mscorlib]System.Type)
    callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Array::GetEnumerator() 
    stloc enumerator
    .try
    {
      CONDITION:
        ldloc enumerator
        callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
        brfalse.s LEAVE

      STATEMENTS:
        // T item = (T)Enumerator.Current
        ldloc enumerator
        callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
        unbox.any !!T
        stloc temp
        ldloca.s temp
        constrained. !!T

        // if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        callvirt instance string [mscorlib]System.Object::ToString()
        callvirt instance string [mscorlib]System.String::ToLower()
        ldarg strValue
        callvirt instance string [mscorlib]System.String::Trim()
        callvirt instance string [mscorlib]System.String::ToLower()
        callvirt instance bool [mscorlib]System.String::Equals(string)
        brfalse.s CONDITION
        ldloc temp
        stloc return_value
        leave.s RETURNVAL

      LEAVE:
        leave.s RETURNDEF
    }
    finally
    {
        // ArrayList's Enumerator may or may not inherit from IDisposable
        ldloc enumerator
        isinst [mscorlib]System.IDisposable
        stloc.s disposer
        ldloc.s disposer
        ldnull
        ceq
        brtrue.s LEAVEFINALLY
        ldloc.s disposer
        callvirt instance void [mscorlib]System.IDisposable::Dispose()
      LEAVEFINALLY:
        endfinally
    }

  RETURNDEF:
    ldarg defaultValue
    stloc return_value

  RETURNVAL:
    ldloc return_value
    ret
  }
} 

如果它是有效的C#,它生成一個看起來像這樣的函數

T GetEnumFromString<T>(string valueString, T defaultValue) where T : Enum

然後使用以下C#代碼:

using MyThing;
// stuff...
private enum MyEnum { Yes, No, Okay }
static void Main(string[] args)
{
    Thing.GetEnumFromString("No", MyEnum.Yes); // returns MyEnum.No
    Thing.GetEnumFromString("Invalid", MyEnum.Okay);  // returns MyEnum.Okay
    Thing.GetEnumFromString("AnotherInvalid", 0); // compiler error, not an Enum
}

不幸的是,這意味著將這部分代碼用MSIL而不是C#編寫,唯一的好處是你能夠通過約束這個方法System.Enum這也是一種無賴,因為它被編譯成一個單獨的程序集。但是,這並不意味著您必須以這種方式部署它。

刪除行.assembly MyThing{}並調用ilasm如下:

ilasm.exe /DLL /OUTPUT=MyThing.netmodule

你得到一個netmodule而不是一個程序集。

不幸的是,VS2010(顯然更早)不支持添加netmodule引用,這意味著在調試時必須將它保留在2個獨立的程序集中。將它們作為程序集的一部分添加的唯一方法是使用/addmodule:{files}命令行參數自行運行csc.exe 在MSBuild腳本中不會痛苦。當然,如果你是勇敢或愚蠢的,你可以每次手動運行csc。當多個程序集需要訪問它時,它肯定會變得更加複雜。

所以,它可以在.Net中完成。值得付出額外的努力嗎?嗯,好吧,我想我會讓你決定那一個。


F#解決方案作為替代

額外信用:事實證明enum除了MSIL之外,至少還有一種其他.NET語言可以使用通用限制:F#。

type MyThing =
    static member GetEnumFromString<'T when 'T :> Enum> str defaultValue: 'T =
        /// protect for null (only required in interop with C#)
        let str = if isNull str then String.Empty else str

        Enum.GetValues(typedefof<'T>)
        |> Seq.cast<_>
        |> Seq.tryFind(fun v -> String.Compare(v.ToString(), str.Trim(), true) = 0)
        |> function Some x -> x | None -> defaultValue

這個更易於維護,因為它是一個完全支持Visual Studio IDE的著名語言,但您仍然需要在解決方案中使用單獨的項目。然而,它自然會產生很大的不同IL(代碼非常不同的),它依賴於FSharp.Core庫,它,就像任何其他外部庫,需要成為你的發行版的一部分。

以下是如何使用它(基本上與MSIL解決方案相同),並顯示它在其他同義結構上正確失敗:

// works, result is inferred to have type StringComparison
var result = MyThing.GetEnumFromString("OrdinalIgnoreCase", StringComparison.Ordinal);
// type restriction is recognized by C#, this fails at compile time
var result = MyThing.GetEnumFromString("OrdinalIgnoreCase", 42);

是的,非常鐵桿。我非常尊重可以在IL中編寫代碼的人,並且知道如何在更高的語言級別支持這些功能 - 我們許多人仍然認為這些級別在應用程序,業務規則,UI,組件庫等下面都是低級別的。 - TonyG 12年3月25日1:31

我真正想知道的是,為什麼C#團隊還沒有開始允許這個,因為它已經得到了MSIL的支持。 - MgSam 12年9月19日0:30

@MgSam - 來自Eric Lippert:沒有特別不尋常的原因; 我們還有許多其他的事情要做,預算有限,而且這個從來沒有超越過“這不是很好嗎?” 語言設計團隊的討論。 - 克里斯托弗·克倫斯12年9月19日0:58

@LordofScripts:我認為原因在於,由於將T限制為System.Enum的類將無法用人們可能期望的T做所有事情,C#的作者認為他們可能完全禁止它。我認為這個決定很不幸,因為C#只是忽略了對System.Enum約束的任何特殊處理,就可以編寫一個HasAnyFlags(這個T it,T other)擴展方法比Enum.HasFlag(Enum)快幾個數量級並且對其參數進行了類型檢查。 - 超級貓2013年4月12日16:27

我不認為我曾經有過一個我沒有在這裡結束的項目。C#6是110%的語法糖,而且沒有進入?切廢話。 - 邁克爾布萊克本2015年10月26日19:40

+1860

C#≥7.3

從C#7.3開始(Visual Studio2017≥v15.7提供),此代碼現在完全有效:

public static TEnum Parse<TEnum>(string value)
where TEnum : struct, Enum { ... }

C#≤7.2

您可以通過濫用約束繼承來獲得真正的編譯器強制枚舉約束。以下代碼同時指定a classstruct約束:

public abstract class EnumClassUtils<TClass>
where TClass : class
{

    public static TEnum Parse<TEnum>(string value)
    where TEnum : struct, TClass
    {
        return (TEnum) Enum.Parse(typeof(TEnum), value);
    }

}

public class EnumUtils : EnumClassUtils<Enum>
{
}

用法:

EnumUtils.Parse<SomeEnum>("value");

注意:這在C#5.0語言規範中有明確說明:

如果類型參數S依賴於類型參數T則:[...] S對於具有值類型約束而T具有引用類型約束是有效的。實際上,這將T限制為System.Object,System.ValueType,System.Enum和任何接口類型。

@ DavidI.McIntosh EnumClassUtils足以將T限制為任何System.Enum和任何派生類型。然後,Parse上的struct將其進一步限制為真實的枚舉類型。您需要在某個時刻限制為Enum。為此,您的類必須嵌套。見gist.github.com/MrJul/7da12f5f2d6c69f03d79 - Julien Lebosquain 2015年4月11日20:51

為了清楚起見,我的評論“不愉快”並不是對你的解決方案的評論 - 它真的是一個美麗的黑客。只是“不愉快”,MS強迫我們使用這樣一個令人費解的黑客。 - 大衛I.麥金托什於2015年4月12日15:08

有沒有辦法將其用於擴展方法? - Mord Zuber 2015年4月29日15:11

我會添加一個內部構造函數,以防止以奇怪的方式繼承此類。 - John Gietzen 2015年5月9日22:01

TClass:class約束在哪裡獲得了什麼? - tsemer於2017年1月4日16:50

+300

編輯

Julien Lebosquain現在已經很好地回答了這個問題我也想延長他的答案ignoreCasedefaultValue和可選的參數,同時加入TryParseParseOrDefault

public abstract class ConstrainedEnumParser<TClass> where TClass : class
// value type constraint S ("TEnum") depends on reference type T ("TClass") [and on struct]
{
    // internal constructor, to prevent this class from being inherited outside this code
    internal ConstrainedEnumParser() {}
    // Parse using pragmatic/adhoc hard cast:
    //  - struct + class = enum
    //  - 'guaranteed' call from derived <System.Enum>-constrained type EnumUtils
    public static TEnum Parse<TEnum>(string value, bool ignoreCase = false) where TEnum : struct, TClass
    {
        return (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase);
    }
    public static bool TryParse<TEnum>(string value, out TEnum result, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T
    {
        var didParse = Enum.TryParse(value, ignoreCase, out result);
        if (didParse == false)
        {
            result = defaultValue;
        }
        return didParse;
    }
    public static TEnum ParseOrDefault<TEnum>(string value, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T
    {
        if (string.IsNullOrEmpty(value)) { return defaultValue; }
        TEnum result;
        if (Enum.TryParse(value, ignoreCase, out result)) { return result; }
        return defaultValue;
    }
}

public class EnumUtils: ConstrainedEnumParser<System.Enum>
// reference type constraint to any <System.Enum>
{
    // call to parse will then contain constraint to specific <System.Enum>-class
}

用法示例:

WeekDay parsedDayOrArgumentException = EnumUtils.Parse<WeekDay>("monday", ignoreCase:true);
WeekDay parsedDayOrDefault;
bool didParse = EnumUtils.TryParse<WeekDay>("clubs", out parsedDayOrDefault, ignoreCase:true);
parsedDayOrDefault = EnumUtils.ParseOrDefault<WeekDay>("friday", ignoreCase:true, defaultValue:WeekDay.Sunday);

通過使用評論和“新”發展,我對Vivek答案舊改進

  • 使用TEnum的清晰度,為用戶
  • 為附加的約束檢查添加更多的接口約束
  • 讓我們TryParse處理ignoreCase現有參數(在VS2010 / .Net 4中引入)
  • 可選使用通用default(在VS2005 / .Net 2中引入)
  • 使用帶有默認值的可選參數(在VS2010 / .Net 4中引入)for defaultValueignoreCase

導致:

public static class EnumUtils
{
    public static TEnum ParseEnum<TEnum>(this string value,
                                         bool ignoreCase = true,
                                         TEnum defaultValue = default(TEnum))
        where TEnum : struct,  IComparable, IFormattable, IConvertible
    {
        if ( ! typeof(TEnum).IsEnum) { throw new ArgumentException("TEnum must be an enumerated type"); }
        if (string.IsNullOrEmpty(value)) { return defaultValue; }
        TEnum lResult;
        if (Enum.TryParse(value, ignoreCase, out lResult)) { return lResult; }
        return defaultValue;
    }
}
+180

您可以為類定義靜態構造函數,該構造函數將檢查類型T是否為枚舉,如果不是則拋出異常。這是Jeffery Richter在他的書CLR中通過C#提到的方法。

internal sealed class GenericTypeThatRequiresAnEnum<T> {
    static GenericTypeThatRequiresAnEnum() {
        if (!typeof(T).IsEnum) {
        throw new ArgumentException("T must be an enumerated type");
        }
    }
}

然後在parse方法中,您可以使用Enum.Parse(typeof(T),input,true)將字符串轉換為枚舉。最後一個真實參數用於忽略輸入的大小寫。

這是泛型類的一個很好的選擇 - 當然,它對泛型方法沒有幫助。 - McGarnagle 2015年2月9日19:18

此外,這也沒有在編譯時強制執行,您只知道在構造函數執行時提供了非Enum T. 雖然這比等待實例構造函數好得多。 - jrh 18年10月9日12:42

+130

還應該考慮到,因為使用Enum約束的C#7.3的發布支持開箱即用而無需進行額外的檢查和填充。

因此,如果您已將項目的語言版本更改為C#7.3,則以下代碼將完美地運行:

    private static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
    {
        // Your code goes here...
    }

如果您不知道如何將語言版本更改為C#7.3,請參閱以下屏幕截圖: 在此輸入圖像描述

編輯1 - 必需的Visual Studio版本並考慮ReSharper

要使Visual Studio能夠識別至少需要15.7版本的新語法。您可以在Microsoft的發行說明中找到它,請參閱Visual Studio 2017 15.7發行說明感謝@MohamedElshawaf指出這個有效的問題。

請注意,在我的案例中,ReSharper 2018.1在編寫此EDIT時尚不支持C#7.3。激活ReSharper會突出顯示Enum約束,因為它告訴我不能使用'System.Array','System.Delegate','System.Enum','System.ValueType','object'作為類型參數約束ReSharper建議快速修復刪除方法類型參數T的'Enum'約束

但是,如果您在工具 - >選項 - > ReSharper Ultimate - >常規下暫時關閉ReSharper,您將看到語法完全沒問題,因為您使用的是VS 15.7或更高版本以及C#7.3或更高版本。

您使用的VS版本是什麼? - mshwf 18年5月11日11:10

這應該是現在接受的答案 - 帕特里克羅伯茨18年5月11日15:33

@MohamedElshawaf我相信它的版本15.7包含對C#7.3的支持 - Patrick Roberts於18年5月11日15:37

我認為最好寫一下T:struct,Enum,以避免將System.Enum本身作為類型參數傳遞。 - Mariusz Pawelski 18年5月11日18:37

就像@MariuszPawelski一樣,我寫了結構,Enum。我的理由在這裡的答案和評論中得到了解釋。 - 斯蒂芬肯尼迪18年1月18日18:52

+110

我通過dimarzionist修改了樣本。此版本僅適用於Enums,不允許結構通過。

public static T ParseEnum<T>(string enumString)
    where T : struct // enum 
    {
    if (String.IsNullOrEmpty(enumString) || !typeof(T).IsEnum)
       throw new Exception("Type given must be an Enum");
    try
    {

       return (T)Enum.Parse(typeof(T), enumString, true);
    }
    catch (Exception ex)
    {
       return default(T);
    }
}

我不會在失敗時返回默認值; 我會讓異常傳播(就像Enum.Parse一樣)。相反,使用TryParse返回bool並使用out參數返回結果。 - 馬克辛普森2010年6月25日21:59

OP希望它不區分大小寫,但事實並非如此。 - Konrad Morawski 12年4月4日6:46

這是不區分大小寫的,這就是最後的“真實”... ... - increddibelly 2013年7月19日11:57

換到哪個?最高的upvoted答案實際上沒有回答這個問題 - 是的,它在其他情況下是有用的,因此是投票,但是如果你堅持使用MS JavaScriptSerializer,它就沒有任何實際用處,因為基本上你是在使用頁面方法和最重要的是,按照問題的要求。接受的答案說不可能。我的回答雖然有點黑客可以完成工作。 - 斯蒂芬肯尼迪2015年6月6日9:21

+90

我試著改進一下代碼:

public T LoadEnum<T>(string value, T defaultValue = default(T)) where T : struct, IComparable, IFormattable, IConvertible
{
    if (Enum.IsDefined(typeof(T), value))
    {
        return (T)Enum.Parse(typeof(T), value, true);
    }
    return defaultValue;
}

這比接受的答案更好,因為它允許你調用defaultValue.ToString(“D”,System.Globalization.NumberFormatInfo.CurrentInfo),即使你不知道它是哪種類型的枚舉,只是該對像是枚舉。 - styfle 17年11月2日15:55

但是,使用IsDefined進行預先檢查會破壞不區分大小寫。與Parse不同,IsDefined沒有ignoreCase參數,MSDN表示它只匹配精確的情況。 - Nyerguds於18年3月6日15:03

+50

我確實有特定的要求,我需要使用enum與enum值相關聯的文本。例如,當我使用enum指定錯誤類型時,它需要描述錯誤詳細信息。

public static class XmlEnumExtension
{
    public static string ReadXmlEnumAttribute(this Enum value)
    {
        if (value == null) throw new ArgumentNullException("value");
        var attribs = (XmlEnumAttribute[]) value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof (XmlEnumAttribute), true);
        return attribs.Length > 0 ? attribs[0].Name : value.ToString();
    }

    public static T ParseXmlEnumAttribute<T>(this string str)
    {
        foreach (T item in Enum.GetValues(typeof(T)))
        {
            var attribs = (XmlEnumAttribute[])item.GetType().GetField(item.ToString()).GetCustomAttributes(typeof(XmlEnumAttribute), true);
            if(attribs.Length > 0 && attribs[0].Name.Equals(str)) return item;
        }
        return (T)Enum.Parse(typeof(T), str, true);
    }
}

public enum MyEnum
{
    [XmlEnum("First Value")]
    One,
    [XmlEnum("Second Value")]
    Two,
    Three
}

 static void Main()
 {
    // Parsing from XmlEnum attribute
    var str = "Second Value";
    var me = str.ParseXmlEnumAttribute<MyEnum>();
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
    // Parsing without XmlEnum
    str = "Three";
    me = str.ParseXmlEnumAttribute<MyEnum>();
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
    me = MyEnum.One;
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
}
+40

希望這有用:

public static TValue ParseEnum<TValue>(string value, TValue defaultValue)
                  where TValue : struct // enum 
{
      try
      {
            if (String.IsNullOrEmpty(value))
                  return defaultValue;
            return (TValue)Enum.Parse(typeof (TValue), value);
      }
      catch(Exception ex)
      {
            return defaultValue;
      }
}

如果你需要不區分大小寫,只需替換return(TValue)Enum.Parse(typeof(TValue),value); by return(TValue)Enum.Parse(typeof(TValue),value,true); - 保羅桑托斯2010年1月28日10:04

+30

有趣的是,顯然這在其他語言中可行的(Managed C ++,IL直接)。

報價:

...兩個約束實際上產生有效的IL,如果用另一種語言編寫,也可以被C#使用(您可以在託管C ++或IL中聲明這些約束)。

誰知道

C ++的託管擴展沒有任何對泛型的支持,我認為你的意思是C ++ / CLI。 - Ben Voigt 2011年3月27日凌晨3:30

+30

這是我的看法。結合答案和MSDN

public static TEnum ParseToEnum<TEnum>(this string text) where TEnum : struct, IConvertible, IComparable, IFormattable
{
    if (string.IsNullOrEmpty(text) || !typeof(TEnum).IsEnum)
        throw new ArgumentException("TEnum must be an Enum type");

    try
    {
        var enumValue = (TEnum)Enum.Parse(typeof(TEnum), text.Trim(), true);
        return enumValue;
    }
    catch (Exception)
    {
        throw new ArgumentException(string.Format("{0} is not a member of the {1} enumeration.", text, typeof(TEnum).Name));
    }
}

MSDN來源

這沒有多大意義。如果TEnum實際上是一個Enum類型但是text是一個空字符串,那麼你會得到一個ArgumentException,說“TEnum必須是一個枚舉類型”,即使它是。 - 尼克8月28日在19:17

+30

從C#<= 7.2開始,現有答案都是正確的。但是,有一個C#語言功能請求(綁定到corefx功能請求)以允許以下內容;

public class MyGeneric<TEnum> where TEnum : System.Enum
{ }

在撰寫本文時,該功能在語言發展會議上進行了“討論”。

編輯

根據nawfal的信息,這是在C#7.3中引入的

有趣的討論,謝謝。雖然(至今)仍然沒有什麼結果 - 約翰克於18年3月27日20:59

@johnc,非常真實,但值得注意,這是一個經常被問到的功能。它的公平賠率。 - DiskJunky 18年3月27日21:00

這將在C#7.3中發布:docs.microsoft.com/en-us/visualstudio/releasenotes / ... :) - nawfal 18年4月11日在17:46

+10

我總是喜歡這個(你可以根據需要修改):

public static IEnumerable<TEnum> GetEnumValues()
{
  Type enumType = typeof(TEnum);

  if(!enumType.IsEnum)
    throw new ArgumentException("Type argument must be Enum type");

  Array enumValues = Enum.GetValues(enumType);
  return enumValues.Cast<TEnum>();
}
+10

我喜歡使用IL的Christopher Currens解決方案,但對於那些不想處理將MSIL包含在構建過程中的棘手業務的人,我在C#中編寫了類似的功能。

請注意,雖然您不能使用通用限制,where T : Enum因為Enum是特殊類型。因此,我必須檢查給定的泛型類型是否真的是枚舉。

我的功能是:

public static T GetEnumFromString<T>(string strValue, T defaultValue)
{
    // Check if it realy enum at runtime 
    if (!typeof(T).IsEnum)
        throw new ArgumentException("Method GetEnumFromString can be used with enums only");

    if (!string.IsNullOrEmpty(strValue))
    {
        IEnumerator enumerator = Enum.GetValues(typeof(T)).GetEnumerator();
        while (enumerator.MoveNext())
        {
            T temp = (T)enumerator.Current;
            if (temp.ToString().ToLower().Equals(strValue.Trim().ToLower()))
                return temp;
        }
    }

    return defaultValue;
}
+10

我已將Vivek的解決方案封裝到您可以重用的實用程序類中。請注意,您仍應在類型上定義類型約束“where T:struct,IConvertible”。

using System;

internal static class EnumEnforcer
{
    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="typeParameterName">Name of the type parameter.</param>
    /// <param name="methodName">Name of the method which accepted the parameter.</param>
    public static void EnforceIsEnum<T>(string typeParameterName, string methodName)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            string message = string.Format(
                "Generic parameter {0} in {1} method forces an enumerated type. Make sure your type parameter {0} is an enum.",
                typeParameterName,
                methodName);

            throw new ArgumentException(message);
        }
    }

    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="typeParameterName">Name of the type parameter.</param>
    /// <param name="methodName">Name of the method which accepted the parameter.</param>
    /// <param name="inputParameterName">Name of the input parameter of this page.</param>
    public static void EnforceIsEnum<T>(string typeParameterName, string methodName, string inputParameterName)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            string message = string.Format(
                "Generic parameter {0} in {1} method forces an enumerated type. Make sure your input parameter {2} is of correct type.",
                typeParameterName,
                methodName,
                inputParameterName);

            throw new ArgumentException(message);
        }
    }

    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="exceptionMessage">Message to show in case T is not an enum.</param>
    public static void EnforceIsEnum<T>(string exceptionMessage)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException(exceptionMessage);
        }
    }
}
+10

我創建了一個擴展方法,to get integer value from enum 看一下方法實現

public static int ToInt<T>(this T soure) where T : IConvertible//enum
{
    if (typeof(T).IsEnum)
    {
        return (int) (IConvertible)soure;// the tricky part
    }
    //else
    //    throw new ArgumentException("T must be an enumerated type");
    return soure.ToInt32(CultureInfo.CurrentCulture);
}

這是用法

MemberStatusEnum.Activated.ToInt()// using extension Method
(int) MemberStatusEnum.Activated //the ordinary way

雖然它可能有效,但它幾乎與問題無關。 - quetzalcoatl於2017年8月1日0:13

+10

如之前的其他答案所述; 雖然這不能用源代碼表示,但它實際上可以在IL Level上完成。@Christopher Currens的答案顯示了IL如何做到這一點。

使用Fody的Add-In ExtraConstraints.Fody有一個非常簡單的方法,完成構建工具,以實現這一目標。只需將他們的nuget包(FodyExtraConstraints.Fody)添加到您的項目中並添加如下約束(摘自ExtraConstraints自述文件):

public void MethodWithEnumConstraint<[EnumConstraint] T>() {...}

public void MethodWithTypeEnumConstraint<[EnumConstraint(typeof(ConsoleColor))] T>() {...}

並且Fody將為要存在的約束添加必要的IL。另請注意約束代表的附加功能:

public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
{...}

public void MethodWithTypeDelegateConstraint<[DelegateConstraint(typeof(Func<int>))] T> ()
{...}

關於Enums,您可能還需要注意非常有趣的Enums.NET

0

如果以後可以使用直接強制轉換,我想你可以System.Enum在必要時使用方法中的基類。您只需要仔細更換類型參數即可。所以方法實現如下:

public static class EnumUtils
{
    public static Enum GetEnumFromString(string value, Enum defaultValue)
    {
        if (string.IsNullOrEmpty(value)) return defaultValue;
        foreach (Enum item in Enum.GetValues(defaultValue.GetType()))
        {
            if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        }
        return defaultValue;
    }
}

然後你就可以使用它:

var parsedOutput = (YourEnum)EnumUtils.GetEnumFromString(someString, YourEnum.DefaultValue);

使用Enum.ToObject()會產生更靈活的結果。添加到其中,您可以進行字符串比較而不區分大小寫,這將無需調用ToLower() - DiskJunky於2002年3月27日21:17

-70

為了完整起見,以下是Java解決方案。我確信在C#中也可以這樣做。它避免了必須在代碼中的任何位置指定類型 - 而是在您嘗試解析的字符串中指定它。

問題是沒有任何方法可以知道String可能匹配的枚舉 - 所以答案就是解決這個問題。

而不是僅接受字符串值,接受具有枚舉和“enumeration.value”形式的值的String。工作代碼如下 - 需要Java 1.8或更高版本。這也會使XML更加精確,就像你會看到像color =“Color.red”而不僅僅是color =“red”這樣的東西。

您可以使用包含枚舉名稱點值名稱的字符串調用acceptEnumeratedValue()方法。

該方法返回正式的枚舉值。

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;


public class EnumFromString {

    enum NumberEnum {One, Two, Three};
    enum LetterEnum {A, B, C};


    Map<String, Function<String, ? extends Enum>> enumsByName = new HashMap<>();

    public static void main(String[] args) {
        EnumFromString efs = new EnumFromString();

        System.out.print("
First string is NumberEnum.Two - enum is " + efs.acceptEnumeratedValue("NumberEnum.Two").name());
        System.out.print("
Second string is LetterEnum.B - enum is " + efs.acceptEnumeratedValue("LetterEnum.B").name());

    }

    public EnumFromString() {
        enumsByName.put("NumberEnum", s -> {return NumberEnum.valueOf(s);});
        enumsByName.put("LetterEnum", s -> {return LetterEnum.valueOf(s);});
    }

    public Enum acceptEnumeratedValue(String enumDotValue) {

        int pos = enumDotValue.indexOf(".");

        String enumName = enumDotValue.substring(0, pos);
        String value = enumDotValue.substring(pos + 1);

        Enum enumeratedValue = enumsByName.get(enumName).apply(value);

        return enumeratedValue;
    }


}
0
votes
answers
34 views
+10

嵌套的字典:按日期組,然後按組枚舉值和彙總計數

2

我的原始數據是這樣的:嵌套的字典:按日期組,然後按組枚舉值和彙總計數

IDictionary<DateTime, IDictionary<ReportType, int>> rawData 

例子:

19/09/2017, [(ReportType.Open, 2)] 
25/09/2017, [(ReportType.Open, 15), (ReportType.Sent, 15)] 
25/10/2017, [(ReportType.Open, 27), (ReportType.Sent, 15)] 
29/10/2017, [(ReportType.Sent, 150)] 

我想達到以下結果:

data for open: 
september-2017: 17 
october-2017: 27 

data for sent: 
september-2017: 15 
october-2017: 165 
----------------------------- 

我試圖

var openData = rawData 
    .Select(g => new { 
     dt = string.Format("{0}/{1}", g.Key.Month, g.Key.Year), 
     g.Key.Year, 
     g.Key.Month, 
     action = g.Value.FirstOrDefault().Key, 
     total = g.Value.Values.Sum() 
    }) 
    .GroupBy(l => new { 
     l.Month, 
     l.Year, 
     action = l.action 
    }) 
    .Where(a => a.Key.action == ReportType.Open) 
    .OrderByDescending(a => a.Key) 
    .ToList(); 

我在做什麼錯?

打印部(series1Series類派生):

foreach (var item in openData) 
{ 
     series1.Points.Add(new DataPoint() 
     { 
      YValues = new double[] { item.total }, 
      AxisLabel = item.dt.ToString(), 
      ToolTip = item.total.ToString() 
     }); 
} 
沙发
0
1

您正在過濾Open reporttype錯誤的地方;

  var openData = rawData 
      .Select(g => new { 
       dt = string.Format("{0}/{1}", g.Key.Month, g.Key.Year), 
       g.Key.Year, 
       g.Key.Month, 
       actions = g.Value.Where(x => x.Key == ReportType.Open) //Filter it here 
      }) 
      .GroupBy(l => new { 
       l.Month, 
       l.Year 
      }) 
      .Select(s => 
      new 
      { 
       s.Key.Month, 
       s.Key.Year, 
       s.Sum(x => x.actions.Sum(sum => sum.Value)) 
      }) 
      .ToList(); 
+0

嘿,感謝您的回答,請參閱約佔總數的總和我的編輯 –

板凳
0
1

有時候,一個簡單的嵌套foreach仍然可以得到最好的&清晰的解決方案。在大多數情況下,大數據集會比LINQ解決方案更快。

Dictionary<string, int> openCounts = new Dictionary<string, int>(); 
Dictionary<string, int> sentCounts = new Dictionary<string, int>(); 

foreach (var kvp in rawData) 
{ 
    //var dt = string.Format("{0}-{1}", kvp.Key.ToString("MMMM"), kvp.Key.Year); 
    var dt = kvp.Key.ToString("MMMM-yyyy"); //simpler alternative, "MMMM" gives full month name 
    foreach (var val in kvp.Value) 
    { 
     if (val.Key == ReportType.Open) 
     { 
      if (openCounts.ContainsKey(dt)) 
       openCounts[dt] += val.Value; 
      else 
       openCounts.Add(dt, val.Value); 
     } 
     else 
     { 
      if (sentCounts.ContainsKey(dt)) 
       sentCounts[dt] += val.Value; 
      else 
       sentCounts.Add(dt, val.Value); 
     } 
    } 
} 
地板
0
1

在您的查詢的第一部分,你正在服用的集合中唯一的第一個項目。那麼你正在分組哪些是錯誤的。 嘗試如下:

 var ob = (from m in rawData 
        where m.Value.Keys.Any(i => i != ReportType.Sent) 
        group m by new { m.Key.Year, m.Key.Month, m.Value.Keys } into gx 
        select new 
        { 
         Date = new DateTime(gx.Key.Year, gx.Key.Month, 1), 
         //       taa = gx.Key.Keys, 
         Total = gx.Select(m => m.Value.Values).ToList().Select(y => y.Sum()).FirstOrDefault() 
        }).ToList(); 
4楼
0
1

我用下面的代碼來實現您所期望的:

var result = rawData 
    .SelectMany(c => c.Value, (b, c) => new { b.Key.Year, Month = b.Key.ToString("MMMM"), c.Key, c.Value }) 
    // At first I unwind nested data 
    .GroupBy(g => g.Key) 
    // Then create my new group by ReportType 
    .Select(c => new { 
     c.Key, 
     Data = c.GroupBy(g => new { g.Year, g.Month }) 
       .Select(f => new { f.Key.Year, f.Key.Month, Sum = f.Sum(s => s.Value) }) 
     // Each ReportType has many groups of monthes of each year 
    }) 
    // At last create my custom type 
    .ToList(); 

C# Fiddle Demo

0
votes
answers
28 views
+10

在打字稿的枚舉中使用字符串變量

0

打字稿中是否可以在枚舉中使用字符串變量? 我可以使用字符串枚舉這樣的:在打字稿的枚舉中使用字符串變量

enum AllDirections { 
    TOP = 'top', 
    BOTTOM = 'bottom', 
    LEFT = 'left', 
    RIGHT = 'right', 
} 

但這代碼:

const top: string = 'top' 
const bottom: string = 'bottom' 
const left: string = 'left' 
const right: string = 'right' 

enum AllDirections { 
    TOP = top, 
    BOTTOM = bottom, 
    LEFT = left, 
    RIGHT = right, 
} 

結果與錯誤:Type 'string' is not assignable to type 'AllDirections'

+0

爲什麼要'頂部*'和*'AllDirections.TOP'? – jonrsharpe

+0

這只是一個錯誤重現的例子。事實上,我試圖從一個文件中導入一個包含所有可用操作的redux動作類型列表,並將它們分配給另一個文件中的枚舉,以便能夠使用此枚舉類型作爲reducer中的類型。 – Anton

沙发
0
1

如果你真的想這樣做,那麼你可以斷言值爲any

enum AllDirections { 
    TOP = top as any, 
    BOTTOM = bottom as any, 
    LEFT = left as any, 
    RIGHT = right as any 
} 

該pr與此相關的是,如果您將這些分配給字符串值,則需要對字符串進行斷言。這不是理想:

let str: string = AllDirections.TOP as any as string; 

或者,這是一個有點冗長,但如果你想成員有正確的類型,你可以考慮使用對象:

// remove the explicit string types so that these are typed 
// as their string literal values 
const top = 'top'; 
const bottom = 'bottom'; 
const left = 'left'; 
const right = 'right'; 

type AllDirections = Readonly<{ 
    TOP: typeof top, 
    BOTTOM: typeof bottom, 
    LEFT: typeof left, 
    RIGHT: typeof right 
}>; 

const AllDirections: AllDirections = { 
    TOP: top, 
    BOTTOM: bottom, 
    LEFT: left, 
    RIGHT: right 
}; 

另一種選擇是翻轉其中字符串存儲:

enum AllDirections { 
    TOP = 'top', 
    BOTTOM = 'bottom', 
    LEFT = 'left', 
    RIGHT = 'right', 
} 

const top = AllDirections.TOP; 
const bottom = AllDirections.BOTTOM; 
const left = AllDirections.LEFT; 
const right = AllDirections.RIGHT; 
+0

第二種解決方案對我來說很完美。謝謝! – Anton

0
votes
answers
16 views
+10

基於枚舉在微調器上更改語言文本

1

我正在開發一個應用程序,使用枚舉來填充微調器以及與它們關聯的圖片。當我嘗試將spinner文本引用到strings.xml以使用手機中設置的語言填充spinner時,我只能獲取數字而不是文本。 getNombres()用於填充主活動中的微調器。基於枚舉在微調器上更改語言文本

下面是代碼:

public enum TipoLugar { 
    OTROS(R.string.otros, R.drawable.otros), 
    RESTAURANTE(R.string.restaurante ,R.drawable.restaurante), 
    BAR(R.string.restaurante , R.drawable.bar), 
    COPAS(R.string.copas , R.drawable.copas), 
    ESPECTACULO(R.string.restaurante , R.drawable.espectaculos), 
    HOTEL(R.string.hotel , R.drawable.hotel), 
    COMPRAS(R.string.compras , R.drawable.compras), 
    EDUCACION(R.string.educacion ,R.drawable.educacion), 
    DEPORTE(R.string.deporte , R.drawable.deporte), 
    NATURALEZA(R.string.naturaleza , R.drawable.naturaleza), 
    GASOLINERA(R.string.gasolinera , R.drawable.gasolinera), 
    VIVIENDA(R.string.vivienda , R.drawable.vivienda), 
    MONUMENTO(R.string.monumento ,R.drawable.monumento); 
    private final int texto; 
    private final int recurso; 

    TipoLugar(int texto,int recurso) { 

     this.texto = texto; 
     this.recurso = recurso; 
     } 

    public String getTexto() { 
     return String.valueOf(texto); 
    } 

    public int getRecurso() { 
     return recurso; 
    } 

    public static String[] getNombres() { 
     String[] resultado = new String[TipoLugar.values().length]; 
     for (TipoLugar tipo : TipoLugar.values()) { 
      resultado[tipo.ordinal()] = String.valueOf(tipo.texto); 
     } 
     return resultado; 
    } } 
沙发
0
0

兩種方式:

首先從你的方法刪除靜態關鍵字,如果它是在MainActivity,改變你的方法爲:

public String[] getNombres() { 
    String[] resultado = new String[TipoLugar.values().length]; 
    for (TipoLugar tipo : TipoLugar.values()) { 
     resultado[tipo.ordinal()] = getString((tipo.texto)); 
    } 
    return resultado; 
} 

第二種方法是保留靜態字,但是現在您每次要調用方法時都必須通過Context

public static String[] getNombres(Context context) { 
    String[] resultado = new String[TipoLugar.values().length]; 
    for (TipoLugar tipo : TipoLugar.values()) { 
     resultado[tipo.ordinal()] = context.getString((tipo.texto)); 
    } 
    return resultado; 
} 

而且你會在你的MainActivity打電話給你的方法是這樣的:

getNombres(this); 

從這裏,你會得到String!而非int因爲你會從琴絃字符串值!

+0

非常感謝。我做了你的第二個建議,並完美地工作 – spcarman

+0

好的@spcarman歡迎您。但是還有一件事是接受我的答案,因爲它解決了你的問題(stackoverflow風格)。點擊答案左側的勾號。你是唯一一個能夠這樣做的人,因爲你問了這個問題! **快樂編碼!**。 – Xenolion

39
votes
answers
32 views
+10

.NET - 枚舉的jSON序列化為字符串

我有一個包含enum屬性的類,在使用時序列化對象時JavaScriptSerializer,我的json結果包含枚舉的整數值而不是它的string“name”。有沒有辦法讓枚舉作為string我的json而不必創建自定義JavaScriptConverter也許有一個屬性,我可以裝飾enum定義,或對象屬性,?

舉個例子:

enum Gender { Male, Female }

class Person
{
    int Age { get; set; }
    Gender Gender { get; set; }
}

期望的json結果:

{ "Age": 35, "Gender": "Male" }
沙发
+90

對於.Net Core Web Api: -

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddJsonFormatters(f => f.Converters.Add(new StringEnumConverter()));
    ...
}

如果這是來自Microsoft.AspNetCore.Mvc.Formatters.Json NuGet包的那個,它似乎只是IMvcCoreBuilder上的擴展方法,而不是IMvcBuilder。所以它的使用類似於services.AddMvcCore()。AddJsonFormatters(f => f.Converters.Add(new StringEnumConverter()));. - infl3x 17年10月27日8:41

板凳
+90

對於ASP.Net核心只需將以下內容添加到您的啟動類:

JsonConvert.DefaultSettings = (() =>
        {
            var settings = new JsonSerializerSettings();
            settings.Converters.Add(new StringEnumConverter { AllowIntegerValues = false });
            return settings;
        });
地板
+70

注意到存在Description屬性時沒有序列化的答案。

這是我的實現,它支持Description屬性。

public class CustomStringEnumConverter : Newtonsoft.Json.Converters.StringEnumConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Type type = value.GetType() as Type;

        if (!type.IsEnum) throw new InvalidOperationException("Only type Enum is supported");
        foreach (var field in type.GetFields())
        {
            if (field.Name == value.ToString())
            {
                var attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
                writer.WriteValue(attribute != null ? attribute.Description : field.Name);

                return;
            }
        }

        throw new ArgumentException("Enum not found");
    }
}

枚舉:

public enum FooEnum
{
    // Will be serialized as "Not Applicable"
    [Description("Not Applicable")]
    NotApplicable,

    // Will be serialized as "Applicable"
    Applicable
}

用法:

[JsonConverter(typeof(CustomStringEnumConverter))]
public FooEnum test { get; set; }
4楼
+50

這是一個老問題,但我認為我會做出貢獻以防萬一。在我的項目中,我為任何Json請求使用單獨的模型。模型通常與具有“Json”前綴的域對象具有相同的名稱。使用AutoMapper映射模型通過讓json模型聲明一個字符串屬性是域類的枚舉,AutoMapper將解析為它的字符串表示。

如果您想知道,我需要Json序列化類的單獨模型,因為內置的序列化器會提供循環引用。

希望這有助於某人。

很高興了解Automapper的這個特性;-) [ScriptIgnore]屬性將刪除循環引用 - ledragon 2013年3月27日16:43

哦。不知道屬性。謝謝!你會在你的Pocos上使用它嗎?我已經使用MetadataType定義來保存任何Poco屬性,只是為了讓它們保持乾淨。屬性是否仍然可以通過元數據工作? - Ales Potocnik Hahonina 2013年4月5日12:50

5楼
+50

為了防止任何人發現上述不足,我最終解決了這個過載問題:

JsonConvert.SerializeObject(objToSerialize, Formatting.Indented, new Newtonsoft.Json.Converters.StringEnumConverter())

這是我當前用例的一個很好的解決方案:我不想更改序列化程序默認值,我在使用屬性時遇到問題,因為我的屬性屬於IList類型。 - Dirk Brockhaus 6月14日8:48

6楼
+30

您實際上可以使用JavaScriptConverter通過內置的JavaScriptSerializer來完成此任務。通過將枚舉轉換為Uri,您可以將其編碼為字符串。

我已經描述瞭如何為日期執行此操作,但它也可以用於枚舉。

http://blog.calyptus.eu/seb/2011/12/custom-datetime-json-serialization/

很有趣的解決方案 感謝分享。 - 奧利弗2012年11月15日9:38

7楼
+10

不確定這是否仍然相關,但我不得不直接寫一個json文件,我想出了以下拼接幾個stackoverflow的答案

public class LowercaseJsonSerializer
{
    private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
    {
        ContractResolver = new LowercaseContractResolver()
    };

    public static void Serialize(TextWriter file, object o)
    {
        JsonSerializer serializer = new JsonSerializer()
        {
            ContractResolver = new LowercaseContractResolver(),
            Formatting = Formatting.Indented,
            NullValueHandling = NullValueHandling.Ignore
        };
        serializer.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
        serializer.Serialize(file, o);
    }

    public class LowercaseContractResolver : DefaultContractResolver
    {
        protected override string ResolvePropertyName(string propertyName)
        {
            return Char.ToLowerInvariant(propertyName[0]) + propertyName.Substring(1);
        }
    }
}

根據json“規則”,它確保我的所有json鍵都是小寫的。格式化它乾淨地縮進並忽略輸出中的空值。通過添加StringEnumConverter,它會打印帶有字符串值的枚舉。

就個人而言,我發現這是我能想到的最乾淨,而不必用註釋弄髒模型。

用法:

    internal void SaveJson(string fileName)
    {
        // serialize JSON directly to a file
        using (StreamWriter file = File.CreateText(@fileName))
        {
            LowercaseJsonSerializer.Serialize(file, jsonobject);
        }
    }
8楼
0

我已經使用Newtonsoft.Json將這個解決方案的所有部分組合在一起它修復了枚舉問題並使錯誤處理更好,並且可以在IIS託管服務中使用。這是很多代碼,所以你可以在GitHub上找到它:https//github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs

你必須添加一些條目Web.config才能使它工作,你可以在這裡看到一個示例文件:https//github.com/jongrant/wcfjsonserializer/blob/master/Web.config

9楼
0

對於VB.net,我發現以下工作:

Dim sec = New Newtonsoft.Json.Converters.StringEnumConverter()
sec.NamingStrategy() = New Serialization.CamelCaseNamingStrategy

Dim JSON_s As New JsonSerializer
JSON_s.Converters.Add(sec)

Dim jsonObject As JObject
jsonObject = JObject.FromObject(SomeObject, JSON_s)
Dim text = jsonObject.ToString

IO.File.WriteAllText(filePath, text)
10楼
-50
new JavaScriptSerializer().Serialize(  
    (from p   
    in (new List<Person>() {  
        new Person()  
        {  
            Age = 35,  
            Gender = Gender.Male  
        }  
    })  
    select new { Age =p.Age, Gender=p.Gender.ToString() }  
    ).ToArray()[0]  
);
_好
126
votes
answers
19 views
+10

將Enum成員序列化為JSON

如何將Python Enum 成員序列化為JSON,以便將生成的JSON反序列化為Python對象?

例如,此代碼:

 來自enum import枚舉導入json類狀態(枚舉):success = 0 json.dumps(Status.success)  

導致錯誤:

  TypeError:&lt; Status.success:0&gt; 不是JSON可序列化的  

我該如何避免這種情況?

up vote 38 down vote accepted favorite
沙发
+380
+50
調用代碼將私有信息(例如應用程序使用的密鑰)保存到不相關的數據庫字段,然後從那裡公開(參見http://chat.stackoverflow.com/transcript/message/35999686#35999686

示例用法:

 &gt ;&GT;&GT; data = {...“action”:“frobnicate”,...“status”:Status.success ...}&gt;&gt;&gt; text = json.dumps(data,cls = EnumEncoder)&gt;&gt;&gt; text'{“status”:{“__enum__”:“Status.success”},“action”:“frobnicate”}'&gt;&gt;&gt; json.loads(text,object_hook = as_enum){'status':&lt;
     
			
        

非常好,謝謝 - Bilal Syed Hussain 2014年6月30日12:35

謝謝,零!很好的例子。 - Ethan Furman 2014年6月30日14:07

如果您在模塊中有代碼(例如enumencoder.py),則必須將從JSON解析的類導入到dict。例如,在這種情況下,您必須在模塊enumencoder.py中導入類Status。 - Francisco Manuel Garca Botella 2016年5月12日16:17

我關注的不是惡意呼叫代碼,而是惡意請求到Web服務器。正如您所提到的,私有數據可以在響應中公開,也可以用於操作代碼流。感謝您更新答案。如果主代碼示例是安全的,那會更好。 - Jared Deckard 2017年3月7日15:39

@JaredDeckard道歉,你是對的,我錯了。我已相應更新答案。感謝您的輸入!這是教育(和懲罰)。 - 零比雷埃夫斯2017年3月7日18:50

+450

正確答案取決於您打算如何處理序列化版本。

如果您要反序列化回Python,請參閱 Zero的回答

如果你的序列化版本是另一種語言,那麼你可能想要使用 IntEnum 代替,這是自動序列化的作為相應的整數:

 來自enum import IntEnum import json class Status(IntEnum):success = 0 failure = 1 json.dumps(Status.success)  < p>並返回: 
 '0' 
     
			
        

我喜歡重新反序化回Python - Bilal Syed Hussain 2014年6月30日2:19

@AShelly:問題是用Python3.4標記的,這個答案是3.4+特定的。 - Ethan Furman 2014年9月3日15:35

完善。如果Enum是一個字符串,你將使用EnumMeta而不是IntEnum - bholagabbar 4月16日'18在7:41

@bholagabbar:不,你會使用Enum,可能還有str mixin - 類MyStrEnum(str,Enum):... - Ethan Furman 18年4月16日20:28

@bholagabbar,很有意思。您應該將解決方案作為答案發布。 - Ethan Furman 18年4月17日16:41

+340

我知道這已經老了,但我覺得這會對人有所幫助。我剛剛解決了這個確切的問題並發現如果你使用字符串枚舉,將你的枚舉聲明為 str 的子類幾乎適用於所有情況:

  import jum from enum import Enum class LogLevel(str,Enum):DEBUG ='DEBUG'INFO ='INFO'print(LogLevel.DEBUG)print(json.dumps(LogLevel.DEBUG))print(json.loads('“DEBUG”) '))print(LogLevel('DEBUG'))  

將輸出:

  LogLevel.DEBUG“DEBUG”DEBUG LogLevel.DEBUG   

正如您所看到的,加載JSON會輸出字符串 DEBUG ,但它很容易轉換回LogLevel對象。

+80

我喜歡Zero Piraeus的回答,但對於使用稱為Boto的Amazon Web Services(AWS)的API進行了稍微修改。

  class EnumEncoder(json.JSONEncoder):def default (self,obj):if isinstance(obj,Enum):return obj.name return json.JSONEncoder.default(self,obj)  

然後我將此方法添加到我的數據模型中:

  def ToJson(self) - &gt; str:return json.dumps(self .__ dict __,cls = EnumEncoder,indent = 1,sort_keys = True)  

我希望這有助於某人。

+10

在Python 3.7中,只需使用 json.dumps(enum_obj,default = str)

0

如果您使用 jsonpickle ,最簡單的方法應如下所示。

 來自enum import Enum import jsonpickle @ jsonpickle.handlers.register(Enum,base = True)類EnumHandler(jsonpickle.handlers.BaseHandler):def flatten(self,obj,data):return obj.value#如果__name__ =='__ main__',則轉換為json友好格式: class Status(Enum):success = 0 error = 1 class SimpleClass:pass simple_class = SimpleClass()simple_class.status = Status.success json = jsonpickle.encode(simple_class,unpicklable = False)print(json)  

在Json序列化後,您將獲得 {“status”:0} 而不是

  {“status”:{“_ _ objclass__”:{“py / type”:“_ _ main __。Status”},“_ name_”:“success”,“_ value_”:0}}  
     
			
        
3
votes
answers
17 views
+10

如何在C ++中使用枚舉

假設我們有enum以下內容: enum Days {Saturday, Sunday, Tuesday,Wednesday, Thursday, Friday}; 我想創建一個這樣的實例enum並用適當的值初始化它,所以我做: Days day = Days.Saturday; 現在我想用現有enum值檢查我的變量或實例,所以我這樣做: if (day == Days.Saturday) { std::cout<<"Ok its Saturday"; } 這給了我一個編譯錯誤: 錯誤:'。'之前的預期primary-expression 代幣 所以要說清楚,說:有什麼區別: if (day == Days.Saturday) //Causes compilation error 和 if (day == Saturday) ? 這兩個實際上是指什麼,一個是好的,一個導致編譯錯誤?
沙发
+20

While C++ (excluding C++11) has enums, the values in them are "leaked" into the global namespace.
If you don't want to have them leaked (and don't NEED to use the enum type), consider the following:

class EnumName {  
   public:   
      static int EnumVal1;  
      (more definitions)  
};  
EnumName::EnumVal1 = {value};  
if ([your value] == EnumName::EnumVal1)  ...
板凳
+10

I think your root issue is the use of . instead of ::, which will use the namespace.

Try:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Days::Saturday;
if(Days::Saturday == day)  // I like literals before variables :)
{
    std::cout<<"Ok its Saturday";
}

這不起作用:要在示例中使用Days :: scope,必須使用枚舉類Days定義枚舉,並使用C ++ 03 + Microsoft擴展或C ++ 11。 - Futal 9月2日'18時8:27

@Futal,上面用Borland C ++ Builder運行。C ++的味道/版本不在問題中。 - James Oravec於18年10月10日19:01

您的Borland C ++ Builder版本必須使用C ++ 11或更高版本。如果您的示例是使用-std = c ++ 98或-std = c ++ 03編譯的,則Gcc和Clang都會給出錯誤或警告。Clang非常清楚:警告:在嵌套名稱說明符中使用枚舉是C ++ 11擴展。 - Futal 9月12日'18時間6:43

地板
-120

First of all, make 'E' in enum, 'e' as a lower case.

Second, drop type name 'Days' in 'Days.Saturday'.

Third ...buy yourself a good C++ book.

對不起,你得到了所有這些下選票(我的意思是,答案確實值得),但這並不意味著你必須離開社區6年。回來加入我們吧。你也有貢獻。很有幫助。分享知識。 - 加布里埃爾史泰博5月7日16:38

0
votes
answers
28 views
+10

如何在C#中枚舉枚舉?

你怎麼能用enumC#枚舉一下? 例如,以下代碼無法編譯: public enum Suit { Spades, Hearts, Clubs, Diamonds } public void EnumerateAllSuitsDemoMethod() { foreach (Suit suit in Suit) { DoSomething(suit); } } 並給出以下編譯時錯誤: 'Suit'是'type',但用作'變量' Suit關鍵字失敗,第二個關鍵字失敗。
0
votes
answers
69 views
+10

Solr中Enum FieldType上的SimpleFacets.lamba和NumberFormatException錯誤

0

有沒有人曾經嘗試過在枚舉字段上排序?我有一個字段定義爲一個具有約1000個不同值的自定義enumConfig.xml文件的枚舉。如果我努力使由它這個枚舉字段或排序範圍查詢,我得到這個異常:Solr中Enum FieldType上的SimpleFacets.lamba和NumberFormatException錯誤

Invalid shift value in prefixCoded bytes (is encoded value really an INT?). 

在配置文件中的值有兩個數字和字符在其中,如果是相關的/重要.. 。

它給了我一個討厭的異常,如果我嘗試小的領域,也:

"msg": "Exception during facet.field: ORBIT", 
"trace": "org.apache.solr.common.SolrException: Exception during 
facet.field: ORBIT
	at org.apache.solr.request.SimpleFacets.lambda$getFacetFieldCounts$0(SimpleFacets.java:766) 

說,這是一個NumberFormatException的......?

儘管我只在指定「facet.limit = -1」時纔會出現該異常,但它適用於較小的值。

沙发
0
0

如果您對枚舉配置文件進行更改(即使您重新編制索引),也會發生類似這樣的奇怪錯誤。我必須徹底刪除我的收藏集才能使其正常工作。