Generic type parameters using out
Im trying to make a universal parser using generic type parameters, but i can't grasp the concept 100%
private bool TryParse<T>(XElement element, string attributeName, out T value) where T : struct
{
if (element.Attribute(attributeName) != null && !string.IsNullOrEmpty(element.Attribute(attributeName).Value))
{
string valueString = element.Attribute(attributeName).Value;
if (typeof(T) == typeof(int))
开发者_Go百科 {
int valueInt;
if (int.TryParse(valueString, out valueInt))
{
value = valueInt;
return true;
}
}
else if (typeof(T) == typeof(bool))
{
bool valueBool;
if (bool.TryParse(valueString, out valueBool))
{
value = valueBool;
return true;
}
}
else
{
value = valueString;
return true;
}
}
return false;
}
As you might guess, the code doesn't compile, since i can't convert int|bool|string to T (eg. value = valueInt). Thankful for feedback, it might not even be possible to way i'm doing it. Using .NET 3.5
The XElement and XAttribute classes both provide a set of explicit conversion operators (casts) to conveniently convert their contents to .NET primitive types.
For example, you can simply do:
XElement elem = // ...
string value1 = (string)elem.Attribute("myString");
int value2 = (int)elem.Attribute("myInt");
int? value3 = (int?)elem.Attribute("myOptionalInt");
bool value4 = (bool)elem.Attribute("myBool");
Seeing as you're only writing a great big if/then combo, I think you'd be better off simply with a bunch of overloads:
public static class Parser
{
private static string TryParseCommon(XElement element, string attributeName)
{
if (element.Attribute(attributeName) != null && !string.IsNullOrEmpty(element.Attribute(attributeName).Value))
{
return element.Attribute(attributeName).Value;
}
return null;
}
public static bool TryParse(XElement element, string attributeName, out string value)
{
value = TryParseCommon(element, attributeName);
return true;
}
public static bool TryParse(XElement element, string attributeName, out int value)
{
return int.TryParse(TryParseCommon(element, attributeName), out value);
}
public static bool TryParse(XElement element, string attributeName, out bool value)
{
return bool.TryParse(TryParseCommon(element, attributeName), out value);
}
}
Not the nicest of things but you can cast your T to something else if you do the cycle over object, i.e. first cast to object then to T, or vice versa. I am not saying anything about whether you get into boxing / unboxing stuff, but the compiler will accept this.
I've done Parse methods before using a TypeConverter
.
TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
try
{
return (T)converter.ConvertFrom(value);
}
This might work for you.
private bool TryParse<T>(XElement element, string attributeName,out T value)
{
if (element.Attribute(attributeName) != null && !string.IsNullOrEmpty(element.Attribute(attributeName).Value))
{
string valueString = element.Attribute(attributeName).Value;
TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
try
{
value = (T)converter.ConvertFrom(valueString);
return true;
}
catch
{
value = default(T);
return false;
}
}
value = default(T);
return false;
}
Maybe this (or any other answer in this question) will help you out.
The problem is that you're trying to make something generic that really isn't. Even if you manually cycle through every type that you yourself can parse, that's not going to allow for all types.
Why don't you try this instead? First define a delegate that describes a general TryParse
method:
public delegate bool TryParser<T>(string text, out T value);
Then restructure your method to take one of these as a parameter:
// uglified code to fit within horizontal scroll area
public bool TryParse<T>
(XElement element, string attributeName, TryParser<T> tryParser, out T value)
{
value = default(T);
if (
element.Attribute(attributeName) != null &&
!string.IsNullOrEmpty(element.Attribute(attributeName).Value)
)
{
string valueString = element.Attribute(attributeName).Value;
return tryParser(valueString, out value);
}
return false;
}
Now, overloading this for all the standard types is quite trivial:
public bool TryParseInt(XElement element, string attributeName, out int value)
{
return TryParse<int>(element, attributeName, int.TryParse, out value);
}
public bool TryParseBool(XElement element, string attributeName, out bool value)
{
return TryParse<bool>(element, attributeName, bool.TryParse, out value);
}
And so on.
What's nice about this approach is that it doesn't even restrict you to using a where T : struct
constraint, or even to the built-in .NET types. A user of this parser class could parse out his/her own custom types from an XML document simply by defining a TryParse
-like method for his/her custom types.
This method I've used in the past might help out some too
public static T ChangeTypeTo<T>(this object value)
{
if (value == null)
return null;
Type underlyingType = typeof (T);
if (underlyingType == null)
throw new ArgumentNullException("value");
if (underlyingType.IsGenericType && underlyingType.GetGenericTypeDefinition()
.Equals(typeof (Nullable<>)))
{
var converter = new NullableConverter(underlyingType);
underlyingType = converter.UnderlyingType;
}
// Guid convert
if (underlyingType == typeof (Guid))
{
return new Guid(value.ToString());
}
// Check for straight conversion or value.ToString conversion
var objType = value.GetType();
// If this is false, lets hope value.ToString can convert otherwise exception
bool objTypeAssignable2typeT = underlyingType.IsAssignableFrom(objType);
// Do conversion
return (T) (objTypeAssignable2typeT ?
Convert.ChangeType(value, underlyingType)
: Convert.ChangeType(value.ToString(), underlyingType));
}
精彩评论