2009-10-23 101 views
4

下面的代碼可以讓您輕鬆傳入一組HtmlParserOptions,然後檢查單個選項以查看它是否被選中。是否有可能創建一個泛型按位枚舉'IsOptionSet()'方法?

[Flags] 
public enum HtmlParserOptions 
{ 
    NotifyOpeningTags = 1, 
    NotifyClosingTags = 2, 
    NotifyText = 4, 
    NotifyEmptyText = 8 
} 

private bool IsOptionSet(HtmlParserOptions options, HtmlParserOptions singleOption) 
{ 
    return (options & singleOption) == singleOption; 
} 

我的問題是,是否有可能創造這樣的通用版本(我猜通過實施該方法屬性的接口),將與任何枚舉與標誌屬性工作?

+0

你可以切換到德爾福 - 這個功能是內置的:) – Ray 2009-10-23 16:07:04

+0

早在那天我就用Delphi了:)有趣的是,Delphi的創建者是微軟帶來創建C#的人 - 所以它是恥辱,他放棄了功能 – 2009-10-26 09:33:55

+0

@射線它回到了4.0! – 2012-06-20 17:50:39

回答

7

編輯:

最簡單,最好的選擇是升級到Beta2的VS2010和使用.NET 4的Enum.HasFlag方法。框架團隊已經爲Enum增加了很多很好的附加功能,使它們更好用。


原件(當前.NET):

你可以通過枚舉做到這一點,而不是仿製藥:

static class EnumExtensions 
{ 
    private static bool IsSignedTypeCode(TypeCode code) 
    { 
     switch (code) 
     { 
      case TypeCode.Byte: 
      case TypeCode.UInt16: 
      case TypeCode.UInt32: 
      case TypeCode.UInt64: 
       return false; 
      default: 
       return true; 
     } 
    } 

    public static bool IsOptionSet(this Enum value, Enum option) 
    { 
     if (IsSignedTypeCode(value.GetTypeCode())) 
     { 
      long longVal = Convert.ToInt64(value); 
      long longOpt = Convert.ToInt64(option); 
      return (longVal & longOpt) == longOpt; 
     } 
     else 
     { 
      ulong longVal = Convert.ToUInt64(value); 
      ulong longOpt = Convert.ToUInt64(option); 
      return (longVal & longOpt) == longOpt; 
     } 
    } 
} 

這完美的作品,像這樣:

class Program 
{ 
    static void Main(string[] args) 
    { 
     HtmlParserOptions opt1 = HtmlParserOptions.NotifyText | HtmlParserOptions.NotifyEmptyText; 
     Console.WriteLine("Text: {0}", opt1.IsOptionSet(HtmlParserOptions.NotifyText)); 
     Console.WriteLine("OpeningTags: {0}", opt1.IsOptionSet(HtmlParserOptions.NotifyOpeningTags)); 

     Console.ReadKey(); 
    } 
} 

上述印刷品:

Text: True 
OpeningTags: False 

不過,它的缺點是它並不能保護您將兩種不同類型的枚舉類型傳入例程。你必須合理地使用它。

+0

它也有很多盒子,有了足夠的工作,你可以使它更有效率和愉快:) – 2009-10-23 16:29:22

+1

是的,真的。它確實有效,並且處理奇怪的情況。好東西已經在.NET 4(Enum.HasFlag)中解決了:http://msdn.microsoft.com /en-us/library/system.enum.hasumg(VS.100).aspx – 2009-10-23 16:50:04

+0

儘管做一些簡單的事情並做到了這一點,但這是一項相當大的工作。 – 2009-10-23 16:51:47

3
public static bool IsOptionSet<T>(this T flags, T option) where T : struct 
{ 
    if(! flags is int) throw new ArgumentException("Flags must be int"); 

    int opt = (int)(object)option; 
    int fl = (int)(object)flags; 
    return (fl & opt) == opt; 
} 

編輯:正如在評論中已經指出的那樣,如果枚舉名不是一個int(這是枚舉默認值)這是不行的。它應該可能被命名爲別的東西來表明這一點,但對大多數情況來說,它可能是'夠用',除非你需要一組超過31個值的標誌。

+0

哈,通過演員對象解決了很多問題,我必須解決反思。 – sisve 2009-10-23 16:13:36

+1

'System.Convert.ToInt32(value)'比轉換更清晰。 – 2009-10-23 16:21:43

+0

這不處理帶有無符號或長存儲類型的枚舉,或者保護您不幸傳遞給它一些其他非枚舉。 (例如:(3.14).IsOptionSet(3)將顯示在intellisense :( – 2009-10-23 16:25:03

3

嗯,有點。

你不能添加一個約束,以確保類型參數是一個「標誌」枚舉,並在純C#中不能添加約束,以確保它是一個枚舉在第一個地方...但是有一點點動作 - 你可以讓後者工作。這是IL中的一個有效約束,但不在C#中。然後,您需要做一些工作來使「和」部分一般工作。

我有一個名爲Unconstrained Melody的項目,它通過一些IL重寫在枚舉上有一些有用的擴展方法。在這種情況下,你會使用:這取決於你想怎麼處理這種情況optionToTest實際上是多個組合標誌的情況下

if (options.HasAny(optionToTest)) 

if (options.HasAll(optionToTest)) 

另外,等待.NET 4.0 - changes in the BCL包括Enum.HasFlag,我認爲它會做你想做的。

+0

喬恩:有沒有一種純粹的C#方法比我的方法做得更好,儘管(在<.net4)中,沒有使用IL重寫? – 2009-10-23 16:56:05

+0

@ Reed:可能不是。幸運的是,UnconstrainedMelody的消費者不需要關心我使用IL重寫 - 他們只是像對待任何其他庫一樣對待它。 – 2009-10-23 17:31:23

+0

當然:)我只是好奇,如果你知道一種方式直接在C#中做到這一點 - 沒有奇怪的副作用。謝謝。 – 2009-10-23 17:38:21

相關問題