【问题标题】:Reinventing a typical 'Get nearest enum value' function to include flag combinations重新发明典型的“获取最近的枚举值”函数以包含标志组合
【发布时间】:2014-12-06 13:13:26
【问题描述】:

场景

我正在使用带有FlagsAttribute 集合的Enum,对于这个问题,我将以System.IO.FileAttributes 枚举为例。

给出一个值,我想得到Enum 中最接近的值,包括标志组合。

例如给出值 10,上面枚举中最接近的值是 4,即FileAttributes.System

但如果我们包含标志组合,那么最接近的值应该是 7,即FileAttributes.ReadOnly, FileAttributes.Hidden, FileAttributes.System

问题

我不假装重新发明轮子,我只是想改进它以考虑可能的标志组合。

那么,在 C# 或 VB.Net 中,我如何编写一种“Get Nearest enum flags-combination”函数?

代码

这是我通常用来获取Enum 的最接近值的方法,这不考虑标志组合。

我已经编写了这个函数,期望它适用于短/ushort/integer/uinteger/long/ulong 枚举,但到那时我还没有在所有场景中测试它。

VB.Net:

''' <summary>
''' Gets the nearest value of an <see cref="T:Enum"/>.
''' </summary>
Private Function GetNearestEnumValue(Of T)(ByVal value As Long) As T
    Return (From enumValue As T
            In [Enum].GetValues(GetType(T)).Cast(Of T)()
            Order By Math.Abs(value - Convert.ToInt64(enumValue))
            ).First
End Function

C#在线翻译:

/// <summary>
/// Gets the nearest value of an <see cref="T:Enum"/>.
/// </summary>
private T GetNearestEnumValue<T>(long value)
{

    return (from enumValue in Enum.GetValues(typeof(T)).Cast<T>()orderby Math.Abs(value - Convert.ToInt64(enumValue))).First;

}

//=======================================================
//Service provided by Telerik (www.telerik.com)
//=======================================================

【问题讨论】:

  • 10 来自哪里,您想将其用作 FileAttr 但它并不代表实际的标志组合?
  • 10 是传递给我想做的函数的虚值:GetNearestValue(Of FileAttributes)(10)
  • 有什么特别的理由在此处包含自动 C# 翻译吗?这似乎......不合适。
  • 该问题被标记为 C# 和 Vb.Net,因此它似乎并不过分,因为一个好的问题必须证明以指定语言给出代码示例的进度,然后有是代码示例。此外,许多人反对或歧视带有 C# 标记的 VB 问题只是因为他们不理解 Vb.Net 问题与 C# 有关,那么代码示例也是为了避免那些人推理。对不起我的英语。
  • 我知道函数是如何工作的。让我换一种方式问:值 8202 的期望答案是什么?

标签: c# .net vb.net enums


【解决方案1】:

可能还有其他方法可以做到这一点,但这是一种可能的解决方案。

从标志枚举的基础值开始(例如 Int32)并向后循环。击中有效标志后,将其存储。再次做同样的事情,但这次向前循环。然后你只需检查哪一个是最近的。我只实现了有符号整数部分,所以我把无符号部分留给你完成。

Imports System.IO
Imports System.Runtime.CompilerServices

Public Module EnumExtension

    <Extension()>
    Public Function GetNearest(Of TEnum As Structure)(ByVal flags As TEnum) As TEnum

        'Get the enum type
        Dim enumType As Type = GetType(TEnum)

        'If it's not an enum, throw(up)
        If (Not enumType.IsEnum) Then
            Throw New InvalidOperationException()
        End If

        'Check if the underlying type of the enum is a 8|16|32|64 bit signed integer:
        If ({GetType(SByte), GetType(Int16), GetType(Int32), GetType(Int64)}.Contains(enumType.GetEnumUnderlyingType())) Then

            'Cast the flags
            Dim value As Int64 = CType(CType(flags, Object), Int64)

            'Get all enum flags
            Dim enumValues As IEnumerable(Of Int64) = (From item In [Enum].GetValues(enumType) Select CType(item, Int64))

            'Get the minimum flag value.
            Dim minSum As Int64 = (From item In enumValues Order By item Ascending Select item).First()

            'Sum all flags to get the highest possible value.
            Dim maxSum As Int64 = enumValues.Sum()

            '..
            Dim lowerValue As Int64
            Dim higherValue As Int64
            Dim tempValue As TEnum = Nothing

            'Get the nearest lower value
            For lowerValue = value To minSum Step -1L
                tempValue = CType([Enum].ToObject(enumType, lowerValue), TEnum)
                If (tempValue.ToString() <> lowerValue.ToString()) Then
                    Exit For
                End If
            Next

            'Get the nearest higher value
            For higherValue = value To maxSum Step +1L
                tempValue = CType([Enum].ToObject(enumType, higherValue), TEnum)
                If (tempValue.ToString() <> higherValue.ToString()) Then
                    Exit For
                End If
            Next

            Debug.WriteLine(String.Format("value: {0}, lower: {1}, higher: {2}", value, lowerValue, higherValue))

            'Return the nearest value.
            If ((value - lowerValue) <= (higherValue - value)) Then
                Return CType([Enum].ToObject(enumType, lowerValue), TEnum)
            Else
                Return CType([Enum].ToObject(enumType, higherValue), TEnum)
            End If

        Else 'If 8|16|32|64 bit unsigned integer aka. (Byte, UInt16, UInt32, UInt64)

            'Todo: work

        End If

    End Function


End Module

测试:

Dim value As FileAttributes = CType(8202, FileAttributes)
Dim nearest As FileAttributes = value.GetNearest()

Debug.WriteLine("value: {0}, flags: {1}", CType(nearest, Integer), nearest.ToString())

输出:

值:8202,较低​​:8199,较高:8208
值:8199,标志:只读、隐藏、系统、NotContentIndexed

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-22
    相关资源
    最近更新 更多