C# string Parsing to variable types - c#

I want to parse a string into a type easily, but I don't want to write wrapper code for each type, I just want to be able to do "1234".Parse() or the like and have it return 1234. This should work for any type that has parsing capabilities.

This trick should work. It uses the type of the variable you're assigning to automagically:
public static class StringExtensions {
public static ParsedString Parse(this string s) {
return new ParsedString(s);
}
}
public class ParsedString {
string str;
public ParsedString(string s) {
str = s;
}
public static implicit operator int(ParsedString s) {
return int.Parse(s.str);
}
public static implicit operator double(ParsedString s) {
return double.Parse(s.str);
}
public static implicit operator short(ParsedString s) {
return short.Parse(s.str);
}
public static implicit operator byte(ParsedString s) {
return byte.Parse(s.str);
}
// ... add other supported types ...
}
Usage:
int p = "1234".Parse();
I would prefer explicitly parsing using framework provided methods rather than relying on these kind of tricks.

My solution works for any type that implements the static method TryParse(string, out T), whether it's a class or a struct. Also, it will work for nullable structs, e.g.
"1234".Parse<int>() == 1234
"asdf".Parse<int>() == 0 // i.e. default(int)
"1234".Parse<int?>() == 1234
"asdf".Parse<int?>() == null
"2001-02-03".Parse<DateTime?>() == new DateTime(2009, 2, 3)
and since System.Net.IPAddress has TryParse,
"127.0.0.1".Parse<IPAddress>().Equals(new IPAddress(new byte[] { 127, 0, 0, 1 }))
I create the code with the System.Linq.Expressions framework, and then cache the created lambda. Since this is done in a generic static class with a specified type, this happens only once per type to parse.
public static class StringExtensions
{
/// <summary>
/// Parse the <paramref name="target"/> as a <typeparamref name="T"/>. If this cannot be achieved, return the default value of <typeparamref name="T"/>.
/// </summary>
/// <typeparam name="T">The type to parse into.</typeparam>
/// <param name="target">The string to parse.</param>
/// <returns>The resultant <typeparamref name="T"/> or the default of <typeparamref name="T"/>.</returns>
/// <example>
/// <code>
/// "1234".Parse&ltint>() == 1234;
/// "a".Parse&ltint>() == 0;
/// "a".Parse&ltint?>() == null;
/// "2010-01-01".Parse<DateTime?>() == new DateTime(2010, 1, 1)
/// "2010-01-01a".Parse<DateTime?>() == null
/// "127.0.0.1".Parse<System.Net.IPAddress>().Equals(new System.Net.IPAddress(new byte[] { 127, 0, 0, 1 }))
/// "".Parse<System.Net.IPAddress>() == null
/// </code>
/// </example>
public static T Parse<T>(this string target)
{
return ParseHelper<T>.Parse(target);
}
/// <summary>
/// Parse the <paramref name="target"/> as a <typeparamref name="T"/>. If this cannot be achieved, return <paramref name="defaultValue"/>
/// </summary>
/// <typeparam name="T">The type to parse into.</typeparam>
/// <param name="target">The string to parse.</param>
/// <param name="defaultValue">The value to return if <paramref name="target"/> could not be parsed.</param>
/// <returns>The resultant <typeparamref name="T"/> or <paramref name="defaultValue"/>.</returns>
/// <example>
/// <code>
/// "1234".Parse&ltint>(-1) == 1234;
/// "a".Parse&ltint>(-1) == -1;
/// "2010-01-01".Parse<DateTime?>(new DateTime(1900, 1, 1)) == new DateTime(2010, 1, 1)
/// "2010-01-01a".Parse<DateTime?>(new DateTime(1900, 1, 1)) == new DateTime(1900, 1, 1)
/// "127.0.0.1".Parse<System.Net.IPAddress>(new System.Net.IPAddress(new byte[] { 0, 0, 0, 0 })).Equals(new System.Net.IPAddress(new byte[] { 127, 0, 0, 1 }))
/// "".Parse<System.Net.IPAddress>(new System.Net.IPAddress(new byte[] { 0, 0, 0, 0 })).Equals(new System.Net.IPAddress(new byte[] { 0, 0, 0, 0 }))
/// </code>
/// </example>
public static T Parse<T>(this string target, T defaultValue)
{
return ParseHelper<T>.Parse(target, defaultValue);
}
private static class ParseHelper<T>
{
private static readonly Func<string, T, T, T> _parser;
static ParseHelper()
{
Type type = typeof(T);
bool isNullable = false;
if (type.GetGenericArguments().Length > 0 && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
isNullable = true;
type = type.GetGenericArguments()[0];
}
ParameterExpression target = Expression.Parameter(typeof(string), "target");
ParameterExpression defaultValue = Expression.Parameter(typeof(T), "defaultValue");
ParameterExpression result = Expression.Parameter(typeof(T), "result");
if (isNullable)
{
Type helper = typeof(NullableParseHelper<>);
helper = helper.MakeGenericType(typeof(T), type);
MethodInfo parseMethod = helper.GetMethod("Parse");
_parser = Expression.Lambda<Func<string, T, T, T>>(
Expression.Call(parseMethod, target, defaultValue),
target, defaultValue, result).Compile();
}
else
{
MethodInfo tryParseMethod = (from m in typeof(T).GetMethods()
where m.Name == "TryParse"
let ps = m.GetParameters()
where ps.Count() == 2
&& ps[0].ParameterType == typeof(string)
&& ps[1].ParameterType == typeof(T).MakeByRefType()
select m).SingleOrDefault();
if (tryParseMethod == null)
{
throw new InvalidOperationException(string.Format("Cannot find method {0}.TryParse(string, out {0})", type.FullName));
}
_parser = Expression.Lambda<Func<string, T, T, T>>(
Expression.Condition(
Expression.Call(tryParseMethod, target, result),
result,
defaultValue),
target, defaultValue, result).Compile();
}
}
public static T Parse(string target)
{
return _parser.Invoke(target, default(T), default(T));
}
public static T Parse(string target, T defaultValue)
{
return _parser.Invoke(target, defaultValue, default(T));
}
private static class NullableParseHelper<TBase> where TBase : struct
{
private static readonly Func<string, TBase?, TBase, TBase?> _parser;
static NullableParseHelper()
{
MethodInfo tryParseMethod = (from m in typeof(TBase).GetMethods()
where m.Name == "TryParse"
let ps = m.GetParameters()
where ps.Count() == 2
&& ps[0].ParameterType == typeof(string)
&& ps[1].ParameterType == typeof(TBase).MakeByRefType()
select m).SingleOrDefault();
if (tryParseMethod == null)
{
throw new InvalidOperationException(string.Format("Cannot find method {0}.TryParse(string, out {0})", typeof(TBase).FullName));
}
ParameterExpression target = Expression.Parameter(typeof(string), "target");
ParameterExpression defaultValue = Expression.Parameter(typeof(TBase?), "defaultValue");
ParameterExpression result = Expression.Parameter(typeof(TBase), "result");
_parser = Expression.Lambda<Func<string, TBase?, TBase, TBase?>>(
Expression.Condition(
Expression.Call(tryParseMethod, target, result),
Expression.ConvertChecked(result, typeof(TBase?)),
defaultValue),
target, defaultValue, result).Compile();
}
public static TBase? Parse(string target, TBase? defaultValue)
{
return _parser.Invoke(target, defaultValue, default(TBase));
}
}
}
}
pants!

Why can't you use the parsing already available?
int.Parse("1234");
decimal.Parse("1234");
double.Parse("1234");
Or use TryParse if you're not sure it will be able to successfully parse.
Or if you wanted to implement it as an extension method, take a look at this article that will show you how to create a Generic String.Parse method.
Edit: I have no idea how that site went down so quickly after I posted my answer. Here is the class that the article created:
using System;
using System.ComponentModel;
public static class Parser {
public static T Parse<T>(this string value) {
// Get default value for type so if string
// is empty then we can return default value.
T result = default(T);
if (!string.IsNullOrEmpty(value)) {
// we are not going to handle exception here
// if you need SafeParse then you should create
// another method specially for that.
TypeConverter tc = TypeDescriptor.GetConverter(typeof(T));
result = (T)tc.ConvertFrom(value);
}
return result;
}
}
Examples:
// regular parsing
int i = "123".Parse<int>();
int? inull = "123".Parse<int?>();
DateTime d = "01/12/2008".Parse<DateTime>();
DateTime? dn = "01/12/2008".Parse<DateTime?>();
// null values
string sample = null;
int? k = sample.Parse<int?>(); // returns null
int l = sample.Parse<int>(); // returns 0
DateTime dd = sample.Parse<DateTime>(); // returns 01/01/0001
DateTime? ddn = sample.Parse<DateTime?>(); // returns null

I know this question is four years old, but still you could consider using this:
public static T Parse<T>(this string target)
{
Type type = typeof(T);
//In case of a nullable type, use the underlaying type:
var ReturnType = Nullable.GetUnderlyingType(type) ?? type;
try
{
//in case of a nullable type and the input text is null, return the default value (null)
if (ReturnType != type && target == null)
return default(T);
return (T)Convert.ChangeType(target, ReturnType);
}
catch
{
return default(T);
}
}

You can do this with a series of .TryParse() if blocks, but you won't be able to do much with it, since this method will have to return type object. So at the call site, you'll just have to attempt to cast it before doing any arithmetic or anything anyway.

You could write a wrapper function that calls tryParse for each type that you want to support.

There are two problems with that scenario. First you would have to write some code to analyse the string to try to determine what data types it would be possible to parse into, and then some logic to choose one of them. (The string "1" for example would be parsable to byte, sbyte, int, uint, long, ulong, float, double and Decimal. Even worse, the string "4.8.12" would be parsable to several numeric types as well as DateTime in three different ways resulting in totally different values...)
The other problem is that any method capable of doing that would have to return the value boxed in an object, so you would still need wrapper code for each data type just to unbox the value.
Besides, what would you do with a value where you have no control over the type? So, don't try to make this simpler, it only gets a lot more complicated.

Related

Generic string to enum extension method with default value [duplicate]

I got an Int16 value, from the database, and need to convert this to an enum type. This is unfortunately done in a layer of the code that knows very little about the objects except for what it can gather through reflection.
As such, it ends up calling Convert.ChangeType which fails with an invalid cast exception.
I found what I consider a smelly workaround, like this:
String name = Enum.GetName(destinationType, value);
Object enumValue = Enum.Parse(destinationType, name, false);
Is there a better way, so that I don't have to move through this String operation?
Here's a short, but complete, program that can be used if anyone need to experiment:
using System;
public class MyClass
{
public enum DummyEnum
{
Value0,
Value1
}
public static void Main()
{
Int16 value = 1;
Type destinationType = typeof(DummyEnum);
String name = Enum.GetName(destinationType, value);
Object enumValue = Enum.Parse(destinationType, name, false);
Console.WriteLine("" + value + " = " + enumValue);
}
}
Enum.ToObject(.... is what you're looking for!
C#
StringComparison enumValue = (StringComparison)Enum.ToObject(typeof(StringComparison), 5);
VB.NET
Dim enumValue As StringComparison = CType([Enum].ToObject(GetType(StringComparison), 5), StringComparison)
If you do a lot of Enum converting try using the following class it will save you alot of code.
public class Enum<EnumType> where EnumType : struct, IConvertible
{
/// <summary>
/// Retrieves an array of the values of the constants in a specified enumeration.
/// </summary>
/// <returns></returns>
/// <remarks></remarks>
public static EnumType[] GetValues()
{
return (EnumType[])Enum.GetValues(typeof(EnumType));
}
/// <summary>
/// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
/// <remarks></remarks>
public static EnumType Parse(string name)
{
return (EnumType)Enum.Parse(typeof(EnumType), name);
}
/// <summary>
/// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
/// </summary>
/// <param name="name"></param>
/// <param name="ignoreCase"></param>
/// <returns></returns>
/// <remarks></remarks>
public static EnumType Parse(string name, bool ignoreCase)
{
return (EnumType)Enum.Parse(typeof(EnumType), name, ignoreCase);
}
/// <summary>
/// Converts the specified object with an integer value to an enumeration member.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
/// <remarks></remarks>
public static EnumType ToObject(object value)
{
return (EnumType)Enum.ToObject(typeof(EnumType), value);
}
}
Now instead of writing (StringComparison)Enum.ToObject(typeof(StringComparison), 5); you can simply write Enum<StringComparison>.ToObject(5);.
Based on the #Peter's answer here is the method for Nullable<int> to Enum conversion:
public static class EnumUtils
{
public static bool TryParse<TEnum>(int? value, out TEnum result)
where TEnum: struct, IConvertible
{
if(!value.HasValue || !Enum.IsDefined(typeof(TEnum), value)){
result = default(TEnum);
return false;
}
result = (TEnum)Enum.ToObject(typeof(TEnum), value);
return true;
}
}
Using EnumUtils.TryParse<YourEnumType>(someNumber, out result) becomes useful for many scenarios. For example, WebApi Controller in Asp.NET does not have default protection against invalid Enum params. Asp.NET will just use default(YourEnumType) value, even if some passes null, -1000, 500000, "garbage string" or totally ignores the parameter. Moreover, ModelState will be valid in all these cases, so one of the solution is to use int? type with custom check
public class MyApiController: Controller
{
[HttpGet]
public IActionResult Get(int? myEnumParam){
MyEnumType myEnumParamParsed;
if(!EnumUtils.TryParse<MyEnumType>(myEnumParam, out myEnumParamParsed)){
return BadRequest($"Error: parameter '{nameof(myEnumParam)}' is not specified or incorrect");
}
return this.Get(washingServiceTypeParsed);
}
private IActionResult Get(MyEnumType myEnumParam){
// here we can guarantee that myEnumParam is valid
}
If you are storing an Enum in a DataTable but don't know which column is an enum and which is a string/int, you can access the value this way:
foreach (DataRow dataRow in myDataTable.Rows)
{
Trace.WriteLine("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=");
foreach (DataColumn dataCol in myDataTable.Columns)
{
object v = dataRow[dataCol];
Type t = dataCol.DataType;
bool e = false;
if (t.IsEnum) e = true;
Trace.WriteLine((dataCol.ColumnName + ":").PadRight(30) +
(e ? Enum.ToObject(t, v) : v));
}
}

Get Value based on String Description Input for C# and .Net [duplicate]

I've got
public enum Als
{
[StringValue("Beantwoord")] Beantwoord = 0,
[StringValue("Niet beantwoord")] NietBeantwoord = 1,
[StringValue("Geselecteerd")] Geselecteerd = 2,
[StringValue("Niet geselecteerd")] NietGeselecteerd = 3,
}
with
public class StringValueAttribute : Attribute
{
private string _value;
public StringValueAttribute(string value)
{
_value = value;
}
public string Value
{
get { return _value; }
}
}
And I would like to put the value from the item I selected of a combobox into a int:
int i = (int)(Als)Enum.Parse(typeof(Als), (string)cboAls.SelectedValue); //<- WRONG
Is this possible, and if so, how? (the StringValue matches the value selected from the combobox).
Here's a helper method that should point you in the right direction.
protected Als GetEnumByStringValueAttribute(string value)
{
Type enumType = typeof(Als);
foreach (Enum val in Enum.GetValues(enumType))
{
FieldInfo fi = enumType.GetField(val.ToString());
StringValueAttribute[] attributes = (StringValueAttribute[])fi.GetCustomAttributes(
typeof(StringValueAttribute), false);
StringValueAttribute attr = attributes[0];
if (attr.Value == value)
{
return (Als)val;
}
}
throw new ArgumentException("The value '" + value + "' is not supported.");
}
And to call it, just do the following:
Als result = this.GetEnumByStringValueAttribute<Als>(ComboBox.SelectedValue);
This probably isn't the best solution though as it's tied to Als and you'll probably want to make this code re-usable. What you'll probably want to strip out the code from my solution to return you the attribute value and then just use Enum.Parse as you are doing in your question.
I'm using the DescriptionAttribute from Microsoft and the following extension method:
public static string GetDescription(this Enum value)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
string description = value.ToString();
FieldInfo fieldInfo = value.GetType().GetField(description);
DescriptionAttribute[] attributes =
(DescriptionAttribute[])
fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
{
description = attributes[0].Description;
}
return description;
}
Here are a couple extension methods that I use for this exact purpose, I've rewritten these to use your StringValueAttribute, but like Oliver I use the DescriptionAttribute in my code.
public static T FromEnumStringValue<T>(this string description) where T : struct {
Debug.Assert(typeof(T).IsEnum);
return (T)typeof(T)
.GetFields()
.First(f => f.GetCustomAttributes(typeof(StringValueAttribute), false)
.Cast<StringValueAttribute>()
.Any(a => a.Value.Equals(description, StringComparison.OrdinalIgnoreCase))
)
.GetValue(null);
}
This can be made slightly more simple in .NET 4.5:
public static T FromEnumStringValue<T>(this string description) where T : struct {
Debug.Assert(typeof(T).IsEnum);
return (T)typeof(T)
.GetFields()
.First(f => f.GetCustomAttributes<StringValueAttribute>()
.Any(a => a.Value.Equals(description, StringComparison.OrdinalIgnoreCase))
)
.GetValue(null);
}
And to call it, just do the following:
Als result = ComboBox.SelectedValue.FromEnumStringValue<Als>();
Conversely, here's a function to get the string from an enum value:
public static string StringValue(this Enum enumItem) {
return enumItem
.GetType()
.GetField(enumItem.ToString())
.GetCustomAttributes<StringValueAttribute>()
.Select(a => a.Value)
.FirstOrDefault() ?? enumItem.ToString();
}
And to call it:
string description = Als.NietBeantwoord.StringValue()
Coming here from duplicate links of this highly upvoted question and answer, here is a method that works with C# 7.3's new Enum type constraint. Note that you also need to specify that it is also a struct so that you can make it the nullable TEnum? or else you will get an error.
public static TEnum? GetEnumFromDescription<TEnum>(string description)
where TEnum : struct, Enum
{
var comparison = StringComparison.OrdinalIgnoreCase;
foreach (var field in typeof(TEnum).GetFields())
{
var attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attribute != null)
{
if (string.Compare(attribute.Description, description, comparison) == 0)
return (TEnum)field.GetValue(null);
}
if (string.Compare(field.Name, description, comparison) == 0)
return (TEnum)field.GetValue(null);
}
return null;
}
Not sure if I am missing something here, can you not do this,
Als temp = (Als)combo1.SelectedItem;
int t = (int)temp;
To parse a string value based on attribute values applied to enum members I'd recommend you use my Enums.NET open source library.
For a custom attribute like the StringValueAttribute you would do this.
Register and store a custom EnumFormat for StringValueAttribute.Value.
Format = Enums.RegisterCustomEnumFormat(m => m.Attributes.Get<StringValueAttribute>()?.Value);
Then use the custom EnumFormat.
Als result = Enums.Parse<Als>("Niet beantwoord", Format); // result == Als.NietBeantwoord
If you were instead using a built-in attribute such as the DescriptionAttribute you could just do this.
Als result = Enums.Parse<Als>("Niet beantwoord", EnumFormat.Description);
In case you're interested, this is how you'd get the string value associated with an enum value.
string value = Als.NietBeantwoord.AsString(Format); // value == "Niet beantwoord"
I made a more generic solution.
You can use it with any attribute and even with other types than string.
using System;
namespace EnumTest
{
public static class EnumExtensions
{
#region Get enum from Attribute
/// <summary>
/// Searches the enum element which has a [attributeType] attribute with a attributePropertyName equivalent to searchValue.
/// Throws exception, if there is no enum element found which has a [attributeType] attribute with a attributePropertyName equivalent to searchValue.
/// </summary>
/// <param name="attributeType">the type of the attribute. e.g. typeof(System.ComponentModel.DescriptionAttribute)</param>
/// <param name="attributePropertyName">the property of the attribute to search. At DescriptionAttribute, this is "Description"</param>
/// <param name="searchValue">the value to search</param>
public static TEnum FromAttributeValueToEnum<TEnum>(Type attributeType, string attributePropertyName, object searchValue)
where TEnum : struct, Enum
{
TEnum? result = FromAttributeValueToNullableEnum<TEnum>(attributeType, attributePropertyName, searchValue);
if (result.HasValue)
{
return result.Value;
}
Type enumType = typeof(TEnum);
throw new ArgumentException($"The enum type {enumType.FullName} has no element with a {attributeType.FullName} with {attributePropertyName} property equivalent to '{searchValue}'");
}
/// <summary>
/// Searches the enum element which has a [attributeType] attribute with a attributePropertyName equivalent to searchValue.
/// Returns fallBackValue, if there is no enum element found which has a [attributeType] attribute with a attributePropertyName equivalent to searchValue.
/// </summary>
/// <param name="attributeType">the type of the attribute. e.g. typeof(System.ComponentModel.DescriptionAttribute)</param>
/// <param name="attributePropertyName">the property of the attribute to search. At DescriptionAttribute, this is "Description"</param>
/// <param name="searchValue">the value to search</param>
public static TEnum FromAttributeValueToEnum<TEnum>(Type attributeType, string attributePropertyName, object searchValue, TEnum fallBackValue)
where TEnum : struct, Enum
{
TEnum? result = FromAttributeValueToNullableEnum<TEnum>(attributeType, attributePropertyName, searchValue);
if (result.HasValue)
{
return result.Value;
}
return fallBackValue;
}
/// <summary>
/// Searches the enum element which has a [attributeType] attribute with a attributePropertyName equivalent to searchValue.
/// Returns null, if there is no enum element found which has a [attributeType] attribute with a attributePropertyName equivalent to searchValue.
/// </summary>
/// <param name="attributeType">the type of the attribute. e.g. typeof(System.ComponentModel.DescriptionAttribute)</param>
/// <param name="attributePropertyName">the property of the attribute to search. At DescriptionAttribute, this is "Description"</param>
/// <param name="searchValue">the value to search</param>
public static TEnum? FromAttributeValueToNullableEnum<TEnum>(Type attributeType, string attributePropertyName, object searchValue)
where TEnum : struct, Enum
{
Type enumType = typeof(TEnum);
if (!enumType.IsEnum)
{
throw new ArgumentException($"The type {enumType.FullName} is no Enum!");
}
if (attributeType == null)
{
throw new ArgumentNullException(nameof(attributeType));
}
if (!typeof(Attribute).IsAssignableFrom(attributeType))
{
throw new ArgumentException($"The type {attributeType.FullName} is no Attribute!", nameof(attributeType));
}
var propertyInfoAttributePropertyName = attributeType.GetProperty(attributePropertyName);
if (propertyInfoAttributePropertyName == null)
{
throw new ArgumentException($"The type {attributeType.FullName} has no (public) property with name {attributePropertyName}", nameof(attributeType));
}
foreach (var field in enumType.GetFields())
{
if (field.IsSpecialName)
{
continue;
}
var attributes = Attribute.GetCustomAttributes(field, attributeType);
if (attributes.Length == 0)
{
continue;
}
foreach (var attribute in attributes)
{
object attributePropertyValue = propertyInfoAttributePropertyName.GetValue(attribute);
if (attributePropertyValue == null)
{
continue;
}
if (attributePropertyValue.Equals(searchValue))
{
return (TEnum)field.GetValue(null);
}
}
}
return null;
}
#endregion
}
}
#RubenHerman's initial post:
Als Beantwoord = EnumExtensions.FromAttributeValueToEnum<Als>(typeof(StringValueAttribute), nameof(StringValueAttribute.Value), "Beantwoord");
Advanced sample:
public class IntValueAttribute : Attribute
{
public IntValueAttribute(int value)
{
Value = value;
}
public int Value { get; private set; }
}
public enum EnumSample
{
[Description("Eins")]
[IntValue(1)]
One,
[Description("Zwei")]
[IntValue(2)]
Two,
[Description("Drei")]
[IntValue(3)]
Three
}
EnumSample one = EnumExtensions.FromAttributeValueToEnum<EnumSample>(typeof(DescriptionAttribute), nameof(DescriptionAttribute.Description), "Eins");
EnumSample two = EnumExtensions.FromAttributeValueToEnum<EnumSample>(typeof(IntValueAttribute), nameof(IntValueAttribute.Value), 2);

C#: "Pretty" type name function?

The name properties of System.Type class return a strange result in case of generic types. Is there a way to get the type name in a format closer to the way I specified it?
Example: typeof(List<string>).OriginalName == "List<string>"
The problem with "pretty" names is they are different depending on the language you are using. Imagine the surprise of a VB.NET developer if OriginalName returned C# syntax.
However, it's pretty fairly easy to make this yourself:
private static string PrettyName(Type type)
{
if (type.GetGenericArguments().Length == 0)
{
return type.Name;
}
var genericArguments = type.GetGenericArguments();
var typeDefinition = type.Name;
var unmangledName = typeDefinition.Substring(0, typeDefinition.IndexOf("`"));
return unmangledName + "<" + String.Join(",", genericArguments.Select(PrettyName)) + ">";
}
This will recursively resolve the unmanaged name, so that if you have something like Dictionary<string, IList<string>> it should still work.
I used CodeDomProvider to convert to c#:
public static string GetOriginalName(this Type type)
{
string TypeName = type.FullName.Replace(type.Namespace + ".", "");//Removing the namespace
var provider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("CSharp"); //You can also use "VisualBasic"
var reference = new System.CodeDom.CodeTypeReference(TypeName);
return provider.GetTypeOutput(reference);
}
Like Harold Hoyer's answer but including nullables and a few more built-in types:
/// <summary>
/// Get full type name with full namespace names
/// </summary>
/// <param name="type">
/// The type to get the C# name for (may be a generic type or a nullable type).
/// </param>
/// <returns>
/// Full type name, fully qualified namespaces
/// </returns>
public static string CSharpName(this Type type)
{
Type nullableType = Nullable.GetUnderlyingType(type);
string nullableText;
if (nullableType != null)
{
type = nullableType;
nullableText = "?";
}
else
{
nullableText = string.Empty;
}
if (type.IsGenericType)
{
return string.Format(
"{0}<{1}>{2}",
type.Name.Substring(0, type.Name.IndexOf('`')),
string.Join(", ", type.GetGenericArguments().Select(ga => ga.CSharpName())),
nullableText);
}
switch (type.Name)
{
case "String":
return "string";
case "Int32":
return "int" + nullableText;
case "Decimal":
return "decimal" + nullableText;
case "Object":
return "object" + nullableText;
case "Void":
return "void" + nullableText;
default:
return (string.IsNullOrWhiteSpace(type.FullName) ? type.Name : type.FullName) + nullableText;
}
}
Here is my implementation. It was created to describe methods, so it handles the ref and out keywords.
private static Dictionary<Type, string> shorthandMap = new Dictionary<Type, string>
{
{ typeof(Boolean), "bool" },
{ typeof(Byte), "byte" },
{ typeof(Char), "char" },
{ typeof(Decimal), "decimal" },
{ typeof(Double), "double" },
{ typeof(Single), "float" },
{ typeof(Int32), "int" },
{ typeof(Int64), "long" },
{ typeof(SByte), "sbyte" },
{ typeof(Int16), "short" },
{ typeof(String), "string" },
{ typeof(UInt32), "uint" },
{ typeof(UInt64), "ulong" },
{ typeof(UInt16), "ushort" },
};
private static string CSharpTypeName(Type type, bool isOut = false)
{
if (type.IsByRef)
{
return String.Format("{0} {1}", isOut ? "out" : "ref", CSharpTypeName(type.GetElementType()));
}
if (type.IsGenericType)
{
if (type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
return String.Format("{0}?", CSharpTypeName(Nullable.GetUnderlyingType(type)));
}
else
{
return String.Format("{0}<{1}>", type.Name.Split('`')[0],
String.Join(", ", type.GenericTypeArguments.Select(a => CSharpTypeName(a)).ToArray()));
}
}
if (type.IsArray)
{
return String.Format("{0}[]", CSharpTypeName(type.GetElementType()));
}
return shorthandMap.ContainsKey(type) ? shorthandMap[type] : type.Name;
}
The calling code looks like this:
string line = String.Format("{0}.{1}({2})",
method.DeclaringType.Name,
method.Name,
String.Join(", ", method.GetParameters().Select(p => CSharpTypeName(p.ParameterType, p.IsOut) + " " + p.Name).ToArray()));
Where method is a MethodInfo instance.
One note: I didn't have a need to describe any multi-dimensional array types, so I didn't bother implementing description for that, but it would be fairly easy to add by calling type.GetArrayRank().
You have to write this yourself. Keep in mind that Type.Name etc. are invoking methods that live in the CLR and can be invoked from multiple languages. This is why they don't come back looking like C# or VB or the language the caller was coded in, but instead looking like the CLR representation.
Note further that string and what not are aliases for CLR types like System.String. Again, this plays a role in the formatting that you see.
It's not hard to do using reflection, but I question the value of it.
A minimal work solution that leverages CodeDomProvider is to control how the CodeTypeReference instance is built in the first place. There are special cases only for generic types and multi-rank arrays, so we only have to care about those:
static CodeTypeReference CreateTypeReference(Type type)
{
var typeName = (type.IsPrimitive || type == typeof(string)) ? type.FullName : type.Name;
var reference = new CodeTypeReference(typeName);
if (type.IsArray)
{
reference.ArrayElementType = CreateTypeReference(type.GetElementType());
reference.ArrayRank = type.GetArrayRank();
}
if (type.IsGenericType)
{
foreach (var argument in type.GetGenericArguments())
{
reference.TypeArguments.Add(CreateTypeReference(argument));
}
}
return reference;
}
Using this modified factory method, it is then possible to use the appropriate code provider to get pretty typing, like so:
using (var provider = new CSharpCodeProvider())
{
var reference = CreateTypeReference(typeof(IObservable<IEnumerable<Tuple<int?, string>>>[,]));
var output = provider.GetTypeOutput(reference);
Console.WriteLine(output);
}
The above code returns IObservable<IEnumerable<Tuple<Nullable<int>, string>>>[,]. The only special case that is not handled well are Nullable types but this is really more a fault of the CodeDomProvider than anything else.
First: Kudos to Navid for wheel reinvention avoidance. I would upvote if I could.
Here are a few points to add, if anyone goes down this path (at least for VS10/.Net 4):
* Try using one of the variants of CodeTypeReference with Type arguments. This avoids a number of pitfalls of using string type names (such as trailing &) and means you get back bool instead of System.Boolean etc. You do get back the full namespace for a lot of types like this but you can always get rid of the namespace part later.
* Simple Nullables tend to come back in the form System.Nullable<int> rather than int? - You can convert to the nicer syntax with a regex on the answer such as:
const string NullablePattern = #"System.Nullable<(?<nulledType>[\w\.]+)>";
const string NullableReplacement = #"${nulledType}?";
answer = Regex.Replace(answer, NullablePattern, NullableReplacement);
* The Type of a method argument like out T? gives a very opaque string. If anyone has an elegant way of dealing with things like this I would love to know about it.Hopefully this all becomes very easy with Roslyn.
as in your example you can expect the type so you can try that
public class test<T> where T : class
{
public List<String> tt
{
get;
set;
}
}
///////////////////////////
test<List<String>> tt = new test<List<String>>();
if(tt.GetType().FullName.Contains(TypeOf(List<String>).FullName))
{
//do something
}
else
{
//do something else
}
I understand that you want to compare types.
The best way to do this is...
myVar is List<string> or
myVar.GetType() == myOtherVar.GetType()
If you don't need this... please disregard my answer.
I understood, that I have to write this myself. Here is my solution (it is actually a bit more than asked for). It is, probably, helpfull.
using System.Reflection;
using HWClassLibrary.Debug;
using System.Collections.Generic;
using System.Linq;
using System;
namespace HWClassLibrary.Helper
{
public static class TypeNameExtender
{
private static IEnumerable<Type> _referencedTypesCache;
public static void OnModuleLoaded() { _referencedTypesCache = null; }
public static string PrettyName(this Type type)
{
if(type == typeof(int))
return "int";
if(type == typeof(string))
return "string";
var result = PrettyTypeName(type);
if(type.IsGenericType)
result = result + PrettyNameForGeneric(type.GetGenericArguments());
return result;
}
private static string PrettyTypeName(Type type)
{
var result = type.Name;
if(type.IsGenericType)
result = result.Remove(result.IndexOf('`'));
if (type.IsNested && !type.IsGenericParameter)
return type.DeclaringType.PrettyName() + "." + result;
if(type.Namespace == null)
return result;
var conflictingTypes = ReferencedTypes
.Where(definedType => definedType.Name == type.Name && definedType.Namespace != type.Namespace)
.ToArray();
var namespaceParts = type.Namespace.Split('.').Reverse().ToArray();
var namespacePart = "";
for(var i = 0; i < namespaceParts.Length && conflictingTypes.Length > 0; i++)
{
namespacePart = namespaceParts[i] + "." + namespacePart;
conflictingTypes = conflictingTypes
.Where(conflictingType => (conflictingType.Namespace + ".").EndsWith(namespacePart))
.ToArray();
}
return namespacePart + result;
}
private static IEnumerable<Type> ReferencedTypes
{
get
{
if(_referencedTypesCache == null)
_referencedTypesCache = Assembly.GetEntryAssembly().GetReferencedTypes();
return _referencedTypesCache;
}
}
private static string PrettyNameForGeneric(Type[] types)
{
var result = "";
var delim = "<";
foreach(var t in types)
{
result += delim;
delim = ",";
result += t.PrettyName();
}
return result + ">";
}
}
}
I do it like this ..
public static class TypeExtensions {
public static String GetName(this Type t) {
String readable;
#if PREVENT_RECURSION
if(m_names.TryGetValue(t, out readable)) {
return readable;
}
#endif
var tArgs = t.IsGenericType ? t.GetGenericArguments() : Type.EmptyTypes;
var name = t.Name;
var format = Regex.Replace(name, #"`\d+.*", "")+(t.IsGenericType ? "<?>" : "");
var names = tArgs.Select(x => x.IsGenericParameter ? "" : GetName(x));
readable=String.Join(String.Join(",", names), format.Split('?'));
#if PREVENT_RECURSION
m_names.Add(t, readable);
#endif
return readable;
}
static readonly Dictionary<Type, String> m_names = new Dictionary<Type, String> { };
}
where PREVENT_RECURSION should be defined true if you don't want the type in generic type arguments be resolved recursively every time, just take from the cache dictionary; leave it undefined of false if you don't care that.
Combined a few answers, added support for nested types and array ranks, and turned it into an extension method with cached resolved names.
/// <summary>
/// Extension methods for <see cref="Type"/>.
/// </summary>
public static class TypeExtensions
{
/// <summary>
/// Dictionary of type names.
/// </summary>
private static readonly ConcurrentDictionary<Type, string> typeNames;
/// <summary>
/// Initializes static members of the <see cref="TypeExtensions"/> class.
/// </summary>
static TypeExtensions()
{
typeNames = new ConcurrentDictionary<Type, string>
{
[typeof(bool)] = "bool",
[typeof(byte)] = "byte",
[typeof(char)] = "char",
[typeof(decimal)] = "decimal",
[typeof(double)] = "double",
[typeof(float)] = "float",
[typeof(int)] = "int",
[typeof(long)] = "long",
[typeof(sbyte)] = "sbyte",
[typeof(short)] = "short",
[typeof(string)] = "string",
[typeof(uint)] = "uint",
[typeof(ulong)] = "ulong",
[typeof(ushort)] = "ushort"
};
}
/// <summary>
/// Gets the type name with generics and array ranks resolved.
/// </summary>
/// <param name="type">
/// The type whose name to resolve.
/// </param>
/// <returns>
/// The resolved type name.
/// </returns>
public static string ToCSTypeName(this Type type)
{
return typeNames.GetOrAdd(type, GetPrettyTypeName);
}
/// <summary>
/// Gets the type name as it would be written in C#
/// </summary>
/// <param name="type">
/// The type whose name is to be written.
/// </param>
/// <returns>
/// The type name as it is written in C#
/// </returns>
private static string GetPrettyTypeName(Type type)
{
var typeNamespace = type.DeclaringType != null ? ToCSTypeName(type.DeclaringType) : type.Namespace;
if (type.IsGenericType)
{
if (type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
return $"{ToCSTypeName(Nullable.GetUnderlyingType(type))}?";
}
var typeList = string.Join(", ", type.GenericTypeArguments.Select(ToCSTypeName).ToArray());
var typeName = type.Name.Split('`')[0];
return $"{typeNamespace}.{typeName}<{typeList}>";
}
if (type.IsArray)
{
var arrayRank = string.Empty.PadLeft(type.GetArrayRank() - 1, ',');
var elementType = ToCSTypeName(type.GetElementType());
return $"{elementType}[{arrayRank}]";
}
return $"{typeNamespace}.{type.Name}";
}
}
public static string pretty_name( Type type, int recursion_level = -1, bool expand_nullable = false )
{
if( type.IsArray )
{
return $"{pretty_name( type.GetElementType(), recursion_level, expand_nullable )}[]";
}
if( type.IsGenericType )
{
// find generic type name
var gen_type_name = type.GetGenericTypeDefinition().Name;
var index = gen_type_name.IndexOf( '`' );
if( index != -1 )
gen_type_name = gen_type_name.Substring( 0, index );
// retrieve generic type aguments
var arg_names = new List<string>();
var gen_type_args = type.GetGenericArguments();
foreach( var gen_type_arg in gen_type_args )
{
arg_names.Add(
recursion_level != 0
? pretty_name( gen_type_arg, recursion_level - 1, expand_nullable )
: "?" );
}
// if type is nullable and want compact notation '?'
if( !expand_nullable && Nullable.GetUnderlyingType( type ) != null )
return $"{arg_names[ 0 ]}?";
// compose common generic type format "T<T1, T2, ...>"
return $"{gen_type_name}<{string.Join( ", ", arg_names )}>";
}
return type.Name;
}
My two cents:
Everything is done through the Type interface
No Regex
No extra objects created except for the list that holds generic argument names
Supports infinite recursion or recursion to a certain level (or no recursion at all!)
Supports nullable types (both formats "Nullable<>" and "?")
Supports ranked arrays

TypeDescriptor CanConvertFrom Bug? or i'm doing it wrong?

This is an extension method taken out from http://dnpextensions.codeplex.com/.
I understand that the string "test" isn't a number string...
I understand that the GetConverter(targetType) is of type int...
What I don't understand is why it say it can convert from a string... but it fail...
/// <summary>
/// Converts an object to the specified target type or returns the default value.
/// </summary>
/// <typeparam name = "T"></typeparam>
/// <param name = "value">The value.</param>
/// <param name = "defaultValue">The default value.</param>
/// <returns>The target type</returns>
public static T ConvertTo<T>(this object value, T defaultValue)
{
if (value != null)
{
var targetType = typeof(T);
var valueType = value.GetType();
if (valueType == targetType) return (T)value;
var converter = TypeDescriptor.GetConverter(value);
if (converter != null)
{
if (converter.CanConvertTo(targetType))
return (T)converter.ConvertTo(value, targetType);
}
converter = TypeDescriptor.GetConverter(targetType);
if (converter != null)
{
if (converter.CanConvertFrom(valueType))
return (T)converter.ConvertFrom(value);
}
}
return defaultValue;
}
[TestMethod]
public void TestConvertToWillFail()
{
// Arrange
var value = "test";
// Act
var result = value.ConvertTo<int>();
// Assert
result.Should().Equal(0);
result.Should().Not.Equal(value);
}
[TestMethod]
public void TestConvertToShouldPass()
{
// Arrange
var value = 123;
var stringValue = "123";
// Act
var stringResult = stringValue.ConvertTo<int>();
// Assert
stringResult.Should().Equal(value);
stringResult.Should().Not.Equal(0);
}
Note: Should() is from Should.codeplex.com
Exception from test:
test is not a valid value for Int32.
What your method is doing in the second call is:
Get the StringConverter
Ask it if it can convert to integers - it answers no
Get the IntegerConverter
Ask it if it can convert from string - it answers yes
Ask it to convert the provided value ("test") - and this is where it blows up, since "test" is indeed not a valid value for Int32.
The CanConvertFrom/To methods are just to verify if the call makes sense at all, not whether the conversion will succeed, since CanConvert works only on the type level
There are strings that will convert to valid integers, but that does not mean that all strings are valid integers, so ConvertFrom/To will throw exceptions, even if the CanConvert return true.
This is my work around.
Please let me know if there's a better version of this out there.
/// <summary>
/// Converts an object to the specified target type or returns the default value.
/// </summary>
/// <typeparam name = "T"></typeparam>
/// <param name = "value">The value.</param>
/// <param name = "defaultValue">The default value.</param>
/// <returns>The target type</returns>
public static T ConvertTo<T>(this object value, T defaultValue)
{
if (value != null)
{
try
{
var targetType = typeof(T);
var valueType = value.GetType();
if (valueType == targetType)
return (T)value;
var converter = TypeDescriptor.GetConverter(value);
if (converter != null)
{
if (converter.CanConvertTo(targetType))
return (T)converter.ConvertTo(value, targetType);
}
converter = TypeDescriptor.GetConverter(targetType);
if (converter != null)
{
if (converter.CanConvertFrom(valueType))
{
return (T)converter.ConvertFrom(value);
}
}
}
catch (Exception e)
{
return defaultValue;
}
}
return defaultValue;
}
ConvertTo can throw exceptions if it is not able to perform the conversion... even if CanConvertTo returns true.
e.g. "12" can be converted to an integer but "test" cannot be. However the converter would say that it can convert from string to integer.

Convert string to nullable type (int, double, etc...)

I am attempting to do some data conversion. Unfortunately, much of the data is in strings, where it should be int's or double, etc...
So what I've got is something like:
double? amount = Convert.ToDouble(strAmount);
The problem with this approach is if strAmount is empty, if it's empty I want it to amount to be null, so when I add it into the database the column will be null. So I ended up writing this:
double? amount = null;
if(strAmount.Trim().Length>0)
{
amount = Convert.ToDouble(strAmount);
}
Now this works fine, but I now have five lines of code instead of one. This makes things a little more difficult to read, especially when I have a large amount of columns to convert.
I thought I'd use an extension to the string class and generic's to pass in the type, this is because it could be a double, or an int, or a long. So I tried this:
public static class GenericExtension
{
public static Nullable<T> ConvertToNullable<T>(this string s, T type) where T: struct
{
if (s.Trim().Length > 0)
{
return (Nullable<T>)s;
}
return null;
}
}
But I get the error: Cannot convert type 'string' to 'T?'
Is there a way around this? I am not very familiar with creating methods using generics.
Another thing to keep in mind is that the string itself might be null.
public static Nullable<T> ToNullable<T>(this string s) where T: struct
{
Nullable<T> result = new Nullable<T>();
try
{
if (!string.IsNullOrEmpty(s) && s.Trim().Length > 0)
{
TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
result = (T)conv.ConvertFrom(s);
}
}
catch { }
return result;
}
You could try using the below extension method:
public static T? GetValueOrNull<T>(this string valueAsString)
where T : struct
{
if (string.IsNullOrEmpty(valueAsString))
return null;
return (T) Convert.ChangeType(valueAsString, typeof(T));
}
This way you can do this:
double? amount = strAmount.GetValueOrNull<double>();
int? amount = strAmount.GetValueOrNull<int>();
decimal? amount = strAmount.GetValueOrNull<decimal>();
What about this:
double? amount = string.IsNullOrEmpty(strAmount) ? (double?)null : Convert.ToDouble(strAmount);
Of course, this doesn't take into account the convert failing.
I wrote this generic type converter. It works with Nullable and standard values, converting between all convertible types - not just string. It handles all sorts of scenarios that you'd expect (default values, null values, other values, etc...)
I've been using this for about a year in dozens of production programs, so it should be pretty solid.
public static T To<T>(this IConvertible obj)
{
Type t = typeof(T);
if (t.IsGenericType
&& (t.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
if (obj == null)
{
return (T)(object)null;
}
else
{
return (T)Convert.ChangeType(obj, Nullable.GetUnderlyingType(t));
}
}
else
{
return (T)Convert.ChangeType(obj, t);
}
}
public static T ToOrDefault<T>
(this IConvertible obj)
{
try
{
return To<T>(obj);
}
catch
{
return default(T);
}
}
public static bool ToOrDefault<T>
(this IConvertible obj,
out T newObj)
{
try
{
newObj = To<T>(obj);
return true;
}
catch
{
newObj = default(T);
return false;
}
}
public static T ToOrOther<T>
(this IConvertible obj,
T other)
{
try
{
return To<T>(obj);
}
catch
{
return other;
}
}
public static bool ToOrOther<T>
(this IConvertible obj,
out T newObj,
T other)
{
try
{
newObj = To<T>(obj);
return true;
}
catch
{
newObj = other;
return false;
}
}
public static T ToOrNull<T>
(this IConvertible obj)
where T : class
{
try
{
return To<T>(obj);
}
catch
{
return null;
}
}
public static bool ToOrNull<T>
(this IConvertible obj,
out T newObj)
where T : class
{
try
{
newObj = To<T>(obj);
return true;
}
catch
{
newObj = null;
return false;
}
}
You might want to try:
TypeConverter conv = TypeDescriptor.GetConverter(typeof(int));
conv.ConvertFrom(mystring);
do your own null check and return int? if necessary. You'll also want to wrap that in a try {}
Give this a shot...
public delegate bool TryParseDelegate<T>(string data, out T output);
public static T? ToNullablePrimitive<T>(this string data,
TryParseDelegate<T> func) where T:struct
{
string.IsNullOrEmpty(data) return null;
T output;
if (func(data, out output))
{
return (T?)output;
}
return null;
}
Then call it like this...
void doStuff()
{
string foo = "1.0";
double? myDouble = foo.ToNullablePrimitive<double>(double.TryParse);
foo = "1";
int? myInt = foo.ToNullablePrimitive<int>(int.TryParse);
foo = "haha";
int? myInt2 = foo.ToNullablePrimitive<int>(int.TryParse);
}
I like Joel's answer, but I've modified it slightly as I'm not a fan of eating exceptions.
/// <summary>
/// Converts a string to the specified nullable type.
/// </summary>
/// <typeparam name="T">The type to convert to</typeparam>
/// <param name="s">The string to convert</param>
/// <returns>The nullable output</returns>
public static T? ToNullable<T>(this string s) where T : struct
{
if (string.IsNullOrWhiteSpace(s))
return null;
TypeConverter conv = TypeDescriptor.GetConverter(typeof (T));
return (T) conv.ConvertFrom(s);
}
/// <summary>
/// Attempts to convert a string to the specified nullable primative.
/// </summary>
/// <typeparam name="T">The primitive type to convert to</typeparam>
/// <param name="data">The string to convert</param>
/// <param name="output">The nullable output</param>
/// <returns>
/// True if conversion is successfull, false otherwise. Null and whitespace will
/// be converted to null and return true.
/// </returns>
public static bool TryParseNullable<T>(this string data, out T? output) where T : struct
{
try
{
output = data.ToNullable<T>();
return true;
}
catch
{
output = null;
return false;
}
}
You can use the following with objects, unfortunately this does not work with strings though.
double? amount = (double?)someObject;
I use it for wrapping a session variable in a property (on a base page).. so my actual usage is (in my base page):
public int? OrganisationID
{
get { return (int?)Session[Constants.Session_Key_OrganisationID]; }
set { Session[Constants.Session_Key_OrganisationID] = value; }
}
I'm able to check for null in page logic:
if (base.OrganisationID == null)
// do stuff
There is no way around this. Nullable, as well as your method, is constrained to using only value types as it's argument. String is a reference type and hence is incompatible with this declaration.
public static class GenericExtension
{
public static T? ConvertToNullable<T>(this String s) where T : struct
{
try
{
return (T?)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(s);
}
catch (Exception)
{
return null;
}
}
}
There is a generic solution (for any type). Usability is good, but implementation should be improved:
http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/
This allows you to write very clean code like this:
string value = null;
int? x = value.ConvertOrDefault<int?>();
and also:
object obj = 1;
string value = null;
int x = 5;
if (value.TryConvert(out x))
Console.WriteLine("TryConvert example: " + x);
bool boolean = "false".ConvertOrDefault<bool>();
bool? nullableBoolean = "".ConvertOrDefault<bool?>();
int integer = obj.ConvertOrDefault<int>();
int negativeInteger = "-12123".ConvertOrDefault<int>();
int? nullableInteger = value.ConvertOrDefault<int?>();
MyEnum enumValue = "SecondValue".ConvertOrDefault<MyEnum>();
MyObjectBase myObject = new MyObjectClassA();
MyObjectClassA myObjectClassA = myObject.ConvertOrDefault<MyObjectClassA>();
Here's something based on accepted answer. I removed the try/catch to make sure all the exceptions are not swallowed and not dealt with. Also made sure that the return variable (in accepted answer) is never initialized twice for nothing.
public static Nullable<T> ToNullable<T>(this string s) where T: struct
{
if (!string.IsNullOrWhiteSpace(s))
{
TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
return (T)conv.ConvertFrom(s);
}
return default(Nullable<T>);
}
My example for anonimous types:
private object ConvertNullable(object value, Type nullableType)
{
Type resultType = typeof(Nullable<>).MakeGenericType(nullableType.GetGenericArguments());
return Activator.CreateInstance(resultType, Convert.ChangeType(value, nullableType.GetGenericArguments()[0]));
}
...
Type anonimousType = typeof(Nullable<int>);
object nullableInt1 = ConvertNullable("5", anonimousType);
// or evident Type
Nullable<int> nullableInt2 = (Nullable<int>)ConvertNullable("5", typeof(Nullable<int>));
Another variation. This one
Does not swallow exceptions
Throws a NotSupportedException if the type can not be converted from string. For instance, a custom struct without a type converter.
Otherwise returns a (T?)null if the string fails to parse. No need to check for null or whitespace.
using System.ComponentModel;
public static Nullable<T> ToNullable<T>(this string s) where T : struct
{
var ret = new Nullable<T>();
var conv = TypeDescriptor.GetConverter(typeof(T));
if (!conv.CanConvertFrom(typeof(string)))
{
throw new NotSupportedException();
}
if (conv.IsValid(s))
{
ret = (T)conv.ConvertFrom(s);
}
return ret;
}
Let's add one more similar solution to the stack. This one also parses enums, and it looks nice. Very safe.
/// <summary>
/// <para>More convenient than using T.TryParse(string, out T).
/// Works with primitive types, structs, and enums.
/// Tries to parse the string to an instance of the type specified.
/// If the input cannot be parsed, null will be returned.
/// </para>
/// <para>
/// If the value of the caller is null, null will be returned.
/// So if you have "string s = null;" and then you try "s.ToNullable...",
/// null will be returned. No null exception will be thrown.
/// </para>
/// <author>Contributed by Taylor Love (Pangamma)</author>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="p_self"></param>
/// <returns></returns>
public static T? ToNullable<T>(this string p_self) where T : struct
{
if (!string.IsNullOrEmpty(p_self))
{
var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));
if (converter.IsValid(p_self)) return (T)converter.ConvertFromString(p_self);
if (typeof(T).IsEnum) { T t; if (Enum.TryParse<T>(p_self, out t)) return t;}
}
return null;
}
https://github.com/Pangamma/PangammaUtilities-CSharp/blob/master/PangammaUtilities/Extensions/ToNullableStringExtension.cs
The generic answer provided by "Joel Coehoorn" is good.
But, this is another way without using those GetConverter... or try/catch blocks... (i'm not sure but this may have better performance in some cases):
public static class StrToNumberExtensions
{
public static short ToShort(this string s, short defaultValue = 0) => short.TryParse(s, out var v) ? v : defaultValue;
public static int ToInt(this string s, int defaultValue = 0) => int.TryParse(s, out var v) ? v : defaultValue;
public static long ToLong(this string s, long defaultValue = 0) => long.TryParse(s, out var v) ? v : defaultValue;
public static decimal ToDecimal(this string s, decimal defaultValue = 0) => decimal.TryParse(s, out var v) ? v : defaultValue;
public static float ToFloat(this string s, float defaultValue = 0) => float.TryParse(s, out var v) ? v : defaultValue;
public static double ToDouble(this string s, double defaultValue = 0) => double.TryParse(s, out var v) ? v : defaultValue;
public static short? ToshortNullable(this string s, short? defaultValue = null) => short.TryParse(s, out var v) ? v : defaultValue;
public static int? ToIntNullable(this string s, int? defaultValue = null) => int.TryParse(s, out var v) ? v : defaultValue;
public static long? ToLongNullable(this string s, long? defaultValue = null) => long.TryParse(s, out var v) ? v : defaultValue;
public static decimal? ToDecimalNullable(this string s, decimal? defaultValue = null) => decimal.TryParse(s, out var v) ? v : defaultValue;
public static float? ToFloatNullable(this string s, float? defaultValue = null) => float.TryParse(s, out var v) ? v : defaultValue;
public static double? ToDoubleNullable(this string s, double? defaultValue = null) => double.TryParse(s, out var v) ? v : defaultValue;
}
Usage is as following:
var x1 = "123".ToInt(); //123
var x2 = "abc".ToInt(); //0
var x3 = "abc".ToIntNullable(); // (int?)null
int x4 = ((string)null).ToInt(-1); // -1
int x5 = "abc".ToInt(-1); // -1
var y = "19.50".ToDecimal(); //19.50
var z1 = "invalid number string".ToDoubleNullable(); // (double?)null
var z2 = "invalid number string".ToDoubleNullable(0); // (double?)0

Categories