Typecasting generic parameters
Using the following code:
Function 开发者_如何学PythonGetSetting(Of T)(ByVal SettingName As String, ByRef DefaultVal As T) As T
Return If(Configuration.ContainsKey(SettingName), CType(Configuration(SettingName), T), DefaultVal)
End Function
Yields the following error:
Value of type 'String' cannot be converted to 'T'.
Any way I could specify that in all cases, the conversion will indeed be possible (I'm basically getting integers, booleans, doubles and strings).
Edit: There seem to be three solutions now:
- Using the `ValueAs` function provided by AMissico
- Casting to an `object`, then to `T`, with a check for null values
- Using a `DirectCast` over Convert.ChangeType
Which would you suggest?
Edit 2: Would this code work?
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
Edit 3: [AMJ] Working Code
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
Quick Test Method
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
The Value(Of T)
and ValueAs
methods support nullable-types. I used Microsoft .NET 2.0 source code as a reference.
This is well-tested and production ready code.
There is no error handling in these "library" functions. It is the responsibility of the caller to handle any conversion errors that occur. The only conversion errors generated are obvious errors, such as trying to convert the string "abc" to 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
Easy, just make it forget that it has a string by casting it to an object first.
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
I know it is late to answer this question but I have been searching something like that and found out that there is another way to do it.
Using IConvertible
interface, and requiring that T
generic type implements the interface. So then we can use Convert.ChangeType()
method.
The code is fairly easy to write then
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
This requires a bit of 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
This code uses reflection to invoke the appropriate conversion method from the IConvertible
interface that all basic value types implement. The result of this conversion can be cast using DirectCast
.
Simplified: This code uses the ToType
method, hence doesn’t require reflection.
Note that the IConvertible
type constraint isn’t even strictly necessary here – since we call the IConvertible
method on the String
return value of the configuration, not the actual type. But the constraint is still useful since this ensures that the appropriate conversion will exist.
You need to put constraints on your method definition to constraint it to types that can be cast to string. See this for adding constraints to generics. You can only constrain to one classthough. I would constrain to object (becuase you can still specify t aas any type derived from the contraint type), then call ToString() instead of casting.
Is the type of Configuration
a Dictionary<string, string>
(c#)?
If yes, you don't need generics.
EDIT: If the Configuration(SettingsName)
returns a string, the container is non-generic & hence you won't need generics here.
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());
}
}
I am sorry, I don't know vb.net enough.
It could be Convert.ChangeType(Configuration(SettingsName), T.GetType())
& then using a DirectCast
over the results.
Hope this helps.
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
精彩评论