类型化通用参数

时间:2010-05-23 12:56:48

标签: vb.net generics casting

使用以下代码:

Function GetSetting(Of T)(ByVal SettingName As String, ByRef DefaultVal As T) As T
    Return If(Configuration.ContainsKey(SettingName), CType(Configuration(SettingName), T), DefaultVal)
End Function

产生以下错误:

Value of type 'String' cannot be converted to 'T'.

任何方式我都可以指定在所有情况下,转换确实是可能的(我基本上得到整数,布尔值,双精度和字符串)。

编辑:现在似乎有三种解决方案:

      
  • 使用AMissico提供的`ValueAs`功能
  •   
  • 转换为`object`,然后转换为`T`,并检查空值
  •   
  • 在Convert.ChangeType
  • 上使用`DirectCast`

你会建议哪个?

编辑2: 这段代码会起作用吗?

Function GetSetting(Of T)(ByVal SettingName As String, Optional ByRef DefaultVal As T = Nothing) As T
    Return If(Configuration.ContainsKey(SettingName), ConvertTo(Of T)(Configuration(SettingName)), DefaultVal)
End Function

Function ConvertTo(Of T)(ByVal Str As String) As T
    Return If(Str Is Nothing Or Str = "", Nothing, CType(CObj(Str), T))
End Function

编辑3: [AMJ]工作代码

Function GetSetting(Of T)(ByVal SettingName As String) As T
    Return GetSetting(Of T)(SettingName, Nothing)
End Function
Function GetSetting(Of T)(ByVal SettingName As String, ByVal DefaultVal As T) As T
    Dim sValue As String = Configuration(SettingName)
    If Len(sValue) = 0 Then
        Return DefaultVal
    Else
        Return CType(CObj(sValue), T)
    End If
End Function

快速测试方法

Public Sub DoIt()

    Me.Configuration.Add("KeyN", Nothing)
    Me.Configuration.Add("KeyE", String.Empty) '""
    Me.Configuration.Add("Key1", "99")
    Me.Configuration.Add("Key2", "1/1/2000")
    Me.Configuration.Add("Key3", "True")
    Me.Configuration.Add("Key4", "0")

    Dim o As Object 'using object in order to see what type is returned by methods

    o = Value(Of Integer)("KeyN", 10) '10
    o = Value(Of Integer)("KeyE", 10) '10
    o = Value(Of Integer)("Key1", 10) '99

    o = Value(Of Date)("KeyN", #11/11/2010#)
    o = Value(Of Date)("KeyE", #11/11/2010#)
    o = Value(Of Date)("Key2", #11/11/2010#)

    o = GetSetting(Of Integer)("KeyN", 10) '10
    o = GetSetting(Of Integer)("KeyE", 10) '10
    o = GetSetting(Of Integer)("Key1", 10) '99

    o = GetSetting(Of Date)("KeyN", #11/11/2010#)
    o = GetSetting(Of Date)("KeyE", #11/11/2010#)
    o = GetSetting(Of Date)("Key2", #11/11/2010#)

    Stop
End Sub

7 个答案:

答案 0 :(得分:4)

Value(Of T)ValueAs方法支持可空类型。我使用Microsoft .NET 2.0源代码作为参考。

这是经过充分测试和生产就绪的代码。

这些“库”功能中没有错误处理。调用者有责任处理发生的任何转换错误。生成的唯一转换错误是明显的错误,例如尝试将字符串“abc”转换为Integer


Public Sub DoIt()
    Dim o As Object
    o = Value(Of Integer)("foo", 10)
    o = Value(Of DateTime)("xxx", #1/1/2000#)
    o = Value(Of Boolean?)("nop", True)
    Stop
End Sub

Public Function GatherTag(ByVal tag As String) As String
    If tag = "foo" Then
        Return "99"
    Else
        Return String.Empty
    End If
End Function

''' <summary>
''' Provides strongly-typed access to the tag values. The method also supports nullable types.
''' </summary>
''' <typeparam name="T">A generic parameter that specifies the return type.</typeparam>
''' <param name="tag">The ExifTool Tag Name,</param>
''' <returns>The value, of type T, of the tag.</returns>
Public Function Value(Of T)(ByVal tag As String, ByVal defaultValue As T) As T
    Return DirectCast(ValueAs(GetType(T), tag, defaultValue), T)
End Function

''' <summary>
''' Returns the tag's value as the specified type. The method also supports nullable types.
''' </summary>
''' <param name="type">The type to return the tag value as.</param>
''' <param name="tag">The ExifTool Tag Name,</param>
''' <returns>The value of the tag as the type requested.</returns>
Public Function ValueAs(ByVal type As System.Type, ByVal tag As String, ByVal defaultValue As Object) As Object
    Dim oResult As Object = Nothing

    Dim oTag As String = GatherTag(tag)

    If Len(oTag) = 0 Then

        'use specified default value

        oResult = defaultValue

    Else

        'is requested type a generic type?

        If type.IsGenericType AndAlso type.GetGenericTypeDefinition Is GetType(Nullable(Of )) Then

            Dim oUnderlyingType As Type = Nullable.GetUnderlyingType(type)

            Dim oConstructed As Type = type.GetGenericTypeDefinition.MakeGenericType(oUnderlyingType)

            Dim oValue As Object

            oValue = System.Convert.ChangeType(oTag, oUnderlyingType)

            If oValue IsNot Nothing Then
                oResult = Activator.CreateInstance(oConstructed, oValue)
            End If

        Else

            'non-generic type

            oResult = System.Convert.ChangeType(oTag, type)

        End If

    End If

    Return oResult
End Function

答案 1 :(得分:1)

这需要一些hocus-pocus:

Public Function GetSetting(Of T As IConvertible)(ByVal SettingName As String, ByRef DefaultVal As T) As T
    Dim formatter As IFormatProvider = CultureInfo.InvariantCulture
    Dim targetType As Type = GetType(T)

    Dim value As IConvertible = Nothing
    Return If(Configuration.TryGetValue(SettingName, value), _
        DirectCast(value.ToType(targetType, formatter), T), _
        DefaultVal)
End Function

此代码使用反射从所有基本值类型实现的IConvertible接口调用适当的转换方法。可以使用DirectCast

来转换此转换的结果

简化:此代码使用ToType方法,因此不需要反射。

请注意,IConvertible类型约束在这里甚至不是必需的 - 因为我们在配置的IConvertible返回值上调用String方法,而不是实际类型。但是约束仍然有用,因为这可以确保存在适当的转换。

答案 2 :(得分:1)

很简单,只需先把它放到一个对象上就可以忘记它有一个字符串。

Function GetSetting(Of T)(ByVal SettingName As String, ByRef DefaultVal As T) As T
    Return If(Configuration.ContainsKey(SettingName), CType(CObj(Configuration(SettingName)), T), DefaultVal)
End Function

答案 3 :(得分:0)

您需要在方法定义上设置约束,以将其约束为可以转换为字符串的类型。有关向泛型添加约束的信息,请参阅this。你只能约束一个classthough。我会约束对象(因为你仍然可以指定任何从约束类型派生的类型),然后调用ToString()而不是强制转换。

答案 4 :(得分:0)

类型ConfigurationDictionary<string, string>(c#)?
如果是,您不需要泛型。

编辑:如果Configuration(SettingsName)返回一个字符串,则该容器是非泛型的&amp;因此,你不需要这里的泛型。

EDIT2:

void Main()
{
    Test<bool>.trythis("false");
    Test<bool>.trythis("true");
    Test<int>.trythis("28");
    Test<decimal>.trythis(decimal.MaxValue.ToString());
}

public class Test<T>
{
  public static void trythis(string value)
    {
        T retVal = (T)Convert.ChangeType(value, typeof(T));
        Console.WriteLine(retVal.GetType());
    }
}

对不起,我不太了解vb.net 可能是Convert.ChangeType(Configuration(SettingsName), T.GetType())&amp;然后在结果上使用DirectCast

希望这有帮助。

答案 5 :(得分:0)

Public Function Value(Of T)(ByVal SettingName As String, ByVal DefaultValue As T) As T
    Return If(Configuration.ContainsKey(SettingName), DirectCast(System.Convert.ChangeType(Configuration(SettingName), GetType(T)), T), DefaultValue)
End Function

答案 6 :(得分:0)

我知道回答这个问题已经很晚了,但我一直在寻找类似的东西,并发现还有另一种方法可以做到。

使用IConvertible接口,并要求T泛型类型实现接口。那么我们就可以使用Convert.ChangeType()方法。

代码很容易编写

Function GetSetting(Of T As IConvertible)(ByVal SettingName As String, _
                                          Optional ByRef DefaultVal As T = Nothing) _
                                          As T
    If Configuration.ContainsKey(SettingName) Then
        Return Convert.ChangeType(Configuration(SettingName), GetType(T))
    End If
    Return DefaultVal
End Function