I often need to update properties using the out
parameter of the standard TryParse
methods. However, use of a property as out
or ref
parameter is not allowed. So, I wrote my own implementation of the TryParse
method MyTryParse
using the generic extension method, which is supposed to work for all standard value types.
Codes:
class Program
{
static void Main(string[] args)
{
new Tester().TestIt();
Console.Read();
}
}
class Tester
{
public bool BooleanP { get; set; } = true;
public char CharP { get; set; } = 'A';
public int IntP { get; set; } = 123;
public double DoubleP { get; set; } = 3.14;
public DateTime DateTimeP { get; set; } = DateTime.Now;
public struct TestStruct
{
public int X { get; set; }
};
public TestStruct StructP { get; set; }
public void TestIt()
{
try
{
// bool.TryParse("TRUE", out BooleanP); // ERROR: A property or indexer may not be passed as out or ref parameter
// bool
BooleanP = BooleanP.MyTryParse("bad");
BooleanP.PPrint("\nBooleanP");
BooleanP = BooleanP.MyTryParse("FALSE");
BooleanP.PPrint("BooleanP");
// char
CharP = CharP.MyTryParse("xx");
CharP.PPrint("\nCharP");
CharP = CharP.MyTryParse("Y");
CharP.PPrint("CharP");
// int
IntP = IntP.MyTryParse("0.1");
IntP.PPrint("\nIntP");
IntP = IntP.MyTryParse("555");
IntP.PPrint("IntP");
// double
DoubleP = DoubleP.MyTryParse("3xxx");
DoubleP.PPrint("\nDoubleP");
DoubleP = DoubleP.MyTryParse("3.1415");
DoubleP.PPrint("DoubleP");
// DateTime
DateTimeP = DateTimeP.MyTryParse("20180707");
DateTimeP.PPrint("\nDateTimeP");
DateTimeP = DateTimeP.MyTryParse("2018.07.07");
DateTimeP.PPrint("DateTimeP");
// struct
Console.WriteLine();
StructP = StructP.MyTryParse("9", true);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
public static class Extensions
{
private enum StandardTtype
{
Undefined,
Boolean,
Byte, SByte,
Char,
Int16, UInt16, Int32, UInt32, Int64, UInt64,
Single, Double, Decimal, DateTime
};
public static T MyTryParse<T>(this T value, string text, bool throwEx = false) where T : struct
{
object oVal = value;
string typeName = typeof(T).Name;
StandardTtype type = StandardTtype.Undefined;
Enum.TryParse(typeName, out type);
switch (type)
{
case StandardTtype.Boolean:
{
if (bool.TryParse(text, out bool outVal))
oVal = outVal;
else ThrowException();
}
break;
case StandardTtype.Byte:
{
if (byte.TryParse(text, out byte outVal))
oVal = outVal;
else ThrowException();
}
break;
case StandardTtype.SByte:
{
if (sbyte.TryParse(text, out sbyte outVal))
oVal = outVal;
else ThrowException();
}
break;
case StandardTtype.Char:
{
if (char.TryParse(text, out char outVal))
oVal = outVal;
else ThrowException();
}
break;
case StandardTtype.Int16:
{
if (short.TryParse(text, out short outVal))
oVal = outVal;
else ThrowException();
}
break;
case StandardTtype.UInt16:
{
if (ushort.TryParse(text, out ushort outVal))
oVal = outVal;
else ThrowException();
}
break;
case StandardTtype.Int32:
{
if (int.TryParse(text, out int outVal))
oVal = outVal;
else ThrowException();
}
break;
case StandardTtype.UInt32:
{
if (uint.TryParse(text, out uint outVal))
oVal = outVal;
else ThrowException();
}
break;
case StandardTtype.Int64:
{
if (long.TryParse(text, out long outVal))
oVal = outVal;
else ThrowException();
}
break;
case StandardTtype.UInt64:
{
if (ulong.TryParse(text, out ulong outVal))
oVal = outVal;
else ThrowException();
}
break;
case StandardTtype.Single:
{
if (float.TryParse(text, out float outVal))
oVal = outVal;
else ThrowException();
}
break;
case StandardTtype.Double:
{
if (double.TryParse(text, out double outVal))
oVal = outVal;
else ThrowException();
}
break;
case StandardTtype.Decimal:
{
if (decimal.TryParse(text, out decimal outVal))
oVal = outVal;
else ThrowException();
}
break;
case StandardTtype.DateTime:
{
if (DateTime.TryParse(text, out DateTime outVal))
oVal = outVal;
else ThrowException();
}
break;
case StandardTtype.Undefined:
default:
if (throwEx)
throw new Exception($"The type '{typeName}' is unhandled");
break;
}
try
{
value = (T)oVal;
}
catch (Exception ex)
{
if (throwEx)
throw new Exception($"Cannot convert '{oVal.ToString()}' into '{typeName}'", ex);
}
void ThrowException()
{
if (throwEx)
throw new Exception($"Cannot parse \"{text}\" into `{typeName}`");
}
return value;
}
public static void PPrint<T>(this T value, string name = "value")
{
Console.WriteLine(name + " = " + value );
}
}
The main purpose of this method is to work quietly (unless I tell to do otherwise) and update properties (or fields) if this is possible.
Question: Is something in the MyTryParse
method which can go wrong?
Any other suggestion for improving performance, style of readability would be very welcome.
out
parameter of the standard because this code it one of the most unconventional and confusing ways of solving something that probalby should be solved in a completely different way. To me it's a clear case of the XY Problem. \$\endgroup\$bool.TryParse("TRUE", out BooleanP);
is not allowed, whereBooleanP
is the property. I have it commented in myTestIt()
method. I am trying to update a property from a string value with a single simple command like thebool.Parse("TRUE");
. Except the last one throws an exception if it is not successful. \$\endgroup\$