简体   繁体   English

C# 中的泛型类型转换

[英]generic type conversion in C#

Is there any way the following code can be improved.有什么办法可以改进以下代码。 I'm aware of the non nullable reference in C# 8.0 and was thinking either that or any other way they code below could be made more robust and better in general.我知道 C# 8.0 中的不可空引用,并且正在考虑以这种方式或任何其他方式编写下面的代码,总体上可以变得更健壮和更好。 This method is called for converting xml data before insertion in a database Through entity framework core.在通过实体框架核心插入数据库之前调用此方法以转换 xml 数据。 Any way these tools can be used to improve this code is welcomed.欢迎使用这些工具来改进此代码的任何方式。

public object Convert(string value, Type toType)
    {
        try
        {
            if (toType == typeof(short))
            {
                return short.Parse(value);
            }
            if (toType == typeof(short?))
            {
                if (string.IsNullOrEmpty(value))
                {
                    return null;
                }
                return short.Parse(value);
            }
            if (toType == typeof(int))
            {
                return int.Parse(value);
            }
            if (toType == typeof(int?))
            {
                if (string.IsNullOrEmpty(value))
                {
                    return null;
                }
                return int.Parse(value);
            }
            if (toType == typeof(decimal))
            {
                return decimal.Parse(value);
            }
            if (toType == typeof(decimal?))
            {
                if (string.IsNullOrEmpty(value))
                {
                    return null;
                }
                return decimal.Parse(value);
            }
            if (toType == typeof(DateTime))
            {
                return DateTime.Parse(value);
            }
            if (toType == typeof(DateTime?))
            {
                if (string.IsNullOrEmpty(value))
                {
                    return null;
                }
                return DateTime.Parse(value);
            }

            throw new NotSupportedException($"No conversion defined for type:'{toType}'");
        }
        catch (System.FormatException excp)
        {
            throw new ConversionException($"Value:'{value}' could not be converted to:'{toType.Name}'", excp);
        }
    }

Many thanks in advance提前谢谢了

The only thing I can offer is to improve robustness by using .TryParse() for the parsing instead of .Parse()我唯一可以提供的是通过使用.TryParse()代替.Parse()来提高稳健性

class Program
{
    static void Main(string[] args)
    {
        var i = Parse<int>("100");
        var x = Parse<double>("3.1417");
        var s = Parse<string>("John");
        var d = Parse<Decimal>("1234.56");
        var f = Parse<DateTime>("4/1/2044");
        var q = Parse<byte>("4A");

        Decimal? p = Parse<decimal>("Not Decimal");
    }

    public static dynamic Parse<T>(string text)
    {
        var toType = typeof(T);
        if (toType == typeof(int))
        {
            if (int.TryParse(text, out int x))
            {
                return x;
            }
        }
        else if (toType == typeof(short))
        {
            if (short.TryParse(text, out short x))
            {
                return x;
            }
        }
        else if (toType == typeof(double))
        {
            if (double.TryParse(text, out double x))
            {
                return x;
            }
        }
        else if (toType == typeof(decimal))
        {
            if (decimal.TryParse(text, out decimal x))
            {
                return x;
            }
        }
        else if (toType == typeof(DateTime))
        {
            if (DateTime.TryParse(text, out DateTime x))
            {
                return x;
            }
        }
        else if (toType == typeof(byte))
        {
            if (byte.TryParse(text, System.Globalization.NumberStyles.HexNumber, null, out byte x))
            {
                return x;
            }
        }
        else if (toType == typeof(string))
        {
            return text;
        }
        return null;
    }
}

IMO the main improvement here would be to remove all boxing. IMO 这里的主要改进是删除所有拳击。 Which means using generics throughout.这意味着始终使用 generics。 Now, there's a complication there: with generics, it is hard to change types without with boxing (to convince the compiler you know what you're doing), or using meta-programming.现在,这里有一个复杂的问题:对于 generics,如果不使用装箱(让编译器相信你知道你在做什么)或使用元编程,很难更改类型。 So: I'd lean towards the latter.所以:我倾向于后者。 Something like:就像是:

public static T Convert<T>(string input)
  => TypeCache<T>.Convert(input);

private static TypeCache<T> {
    public static readonly Func<string, T> Convert
    = CreateConverter<T>();
}
private static Func<string, T> CreateConverter<T>()
{...}

The magic all happens in that last method.魔术都发生在最后一种方法中。 The problem is: it isn't trivial .问题是:这不是微不足道的。 The simplest approach should be to use reflection to discover a suitable parse method, then manually construct an Expression<Func<string, T>> by linking appropriate Expression nodes to repreresent the operation, then call Compile() to get a delegate.最简单的方法应该是使用反射来发现合适的解析方法,然后通过链接适当的Expression节点来表示操作来手动构造Expression<Func<string, T>>来表示操作,然后调用Compile()来获取委托。 Another approach is to go straight to DynamicMethod and ILGenerator .另一种方法是 go 直接到DynamicMethodILGenerator Both are advanced topics.两者都是高级主题。

This is the code I wrote and used in my company's project really well.这是我在我公司的项目中编写和使用得很好的代码。

You can try to use this:你可以尝试使用这个:

/// <summary>
/// Method : Simply Universal Type Converter
/// Requirement : C# 7.0+
/// Created by : Byungho Park(Tapegawui) in South Korea.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="val">Original value</param>
/// <param name="rfrom">(Optional)Character(s) want to replace from</param>
/// <param name="rto">(Optional)Character(s) will be replace to</param>
/// <returns></returns>
public static T Cast<T>(dynamic val, string rfrom = "", string rto = "") where T : IConvertible
{
    try
    {
        // Convert null to empty else 0
        if (val is null || val.Equals(DBNull.Value))
        {
            if (typeof(T) == typeof(string) || typeof(T) == typeof(DateTime))
            {
                val = string.Empty;
            }
            else if (typeof(T) == typeof(bool))
            {
                val = false;
            }
            else
            {
                val = 0;
            }
        }
        else
        {
            // Replace string given parameter from a to b
            if (typeof(T) == typeof(string) && rfrom == "" & rto.Length > 0)
            {
                if (val.ToString().Length == 0)
                {
                    val = rto;
                }
            }
            else if (typeof(T) == typeof(string) && rto.Length > 0)
            {
                val = (string)val.ToString().Replace(rfrom, rto);
            }
        }

        // Convert type on this block finally
        return (T)Convert.ChangeType(val, typeof(T));
    }
    catch (Exception)
    {
        return default(T);
    }
}

And usage examples:和用法示例:

using System;

int vint = 10;
int vint2 = vint;
string vstr = "1000000";
string vdcm = "123456789123456789";

for (int i = 1; i <= vint; i++)
{
    vint2 += i;
}
Console.WriteLine($"Adding int with loop : {vint2} from {vint}\n");

string tint = Cast<string>(vint);
for (int i = 1; i <= vint; i++)
{
    tint += i;
}
Console.WriteLine($"Adding string with loop : {tint} from {vint}\n");

long tlong = Cast<long>(vstr);
tlong *= tlong;
Console.WriteLine($"Multiply long : {tlong} from {vstr}\n");

double tdbl = Cast<double>(vdcm);
for (int i = 1; i <= vint; i++)
{
    tdbl *= i;
}
Console.WriteLine($"Multiply double with loop : {tdbl} from {vdcm}\n");

decimal tdcm = Cast<decimal>(vdcm);
for (int i = 1; i <= vint; i++)
{
    tdcm *= i;
}
Console.WriteLine($"Multiply decimal with loop : {tdcm} from {vdcm}\n");

string ns = null;
Console.WriteLine($"Null string : {Cast<string>(ns)}\n");

int? ni = null;
Console.WriteLine($"Null int : {Cast<int>(ni)}\n");

long? nl = null;
Console.WriteLine($"Null long : {Cast<long>(nl)}\n");

double? ndbl = null;
Console.WriteLine($"Null double : {Cast<double>(ndbl)}\n");

decimal? nd = null;
Console.WriteLine($"Null decimal : {Cast<decimal>(nd)}\n");

string tb = "true";
Console.WriteLine($"Convert string to boolean : {Cast<bool>(tb)}\n");

bool? nb = null;
Console.WriteLine($"Null boolean : {Cast<bool>(nb)}\n");

// -----------------------
// From Microsoft examples
double d = -2.345;
int t = Cast<int>(d);

Console.WriteLine($"The double value {d} when converted to an int becomes {t}\n");

string s = "98/12/12";
DateTime dt = Cast<DateTime>(s);

Console.WriteLine($"The string value {s} when converted to a Date becomes {dt}\n");
// -----------------------

// ------------------------------------------
// Replace some character(s) with string type
string rs = "Replace this string with x to y.";
Console.WriteLine($"{Cast<string>(rs, " ", "_")}\n");

Console.WriteLine($"{Cast<string>("abcd", "", "abc")}\n");
Console.WriteLine($"{Cast<string>("", "", "abc")}\n");

string rs3 = "Replace this string from x to y.";
string ts = "string";
Console.WriteLine($"{Cast<string>(rs3, ts, ts.ToUpper())}\n");

Console.WriteLine($"Replace int character with string : {Cast<string>(vstr, "0", "1")}\n");
// ------------------------------------------

Console.WriteLine("Press any key to close...");
Console.ReadKey();

In addition, output results:另外,output 结果:

Adding int with loop : 65 from 10

Adding string with loop : 1012345678910 from 10

Multiply long : 1000000000000 from 1000000

Multiply double with loop : 4.479999963712E+23 from 123456789123456789

Multiply decimal with loop : 447999996371199995923200 from 123456789123456789

Null string :

Null int : 0

Null long : 0

Null double : 0

Null decimal : 0

Convert string to boolean : True

Null boolean : False

The double value -2.345 when converted to an int becomes -2

The string value 98/12/12 when converted to a Date becomes 1998-12-12 오전 12:00:00

Replace_this_string_with_x_to_y.

abcd

abc

Replace this STRING from x to y.

Replace int character with string : 1111111

Press any key to close...
public static class StringExtensions
{
    public static TDest ConvertStringTo<TDest>(this string src)
    {
        if (src == null)
        {
            return default(TDest);
        }

        try
        {
            return ChangeType<TDest>(src);
        }
        catch
        {
            return default(TDest);
        }
    }

    private static T ChangeType<T>(string value)
    {
        var t = typeof(T);
        if (t.GetTypeInfo().IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
        {
            t = Nullable.GetUnderlyingType(t);
        }

        return (T)Convert.ChangeType(value, t);
    }
}

Using runtime infrastructure to determine types and perform conversions between them as proposed by the other answers is very convenient and probably what you are looking for.使用运行时基础结构来确定类型并按照其他答案的建议执行它们之间的转换非常方便,并且可能是您正在寻找的。

If, however, you need more control over your conversion (or rather parsing), eg because you get weird input formats that require pre-processing, may I suggest the following class.但是,如果您需要更多地控制转换(或者更确切地说是解析),例如因为您得到需要预处理的奇怪输入格式,我可以建议以下 class。

It lets you provide a parser method for each type you register.它允许您为注册的每种类型提供一个解析器方法。 The types from your question with their nullable cousins come pre-registered in the constructor, but you can also add any other method to the dictionary as your input data requires:您的问题中的类型及其可空表亲已在构造函数中预先注册,但您也可以根据输入数据的需要将任何其他方法添加到字典中:

public delegate bool Parser<T>(string input, out T output);

public class Parsers
{
    private delegate bool UntypedParser(string input, out object output);

    private Dictionary<Type, UntypedParser> _parsersByType = new Dictionary<Type, UntypedParser>();

    /// <summary>
    /// Creates a <see cref="Parsers"/> instance pre-populated with parsers for the most common types.
    /// </summary>
    public static Parsers CreateDefault()
    {
        Parsers result = new Parsers();
        result.AddParser<string>((string input, out string output) => { output = input; return true; });
        result.AddParserForNullable<bool>(bool.TryParse);
        result.AddParserForNullable<byte>(byte.TryParse);
        result.AddParserForNullable<DateTime>(DateTime.TryParse);
        result.AddParserForNullable<decimal>(decimal.TryParse);
        result.AddParserForNullable<double>(double.TryParse);
        result.AddParserForNullable<float>(float.TryParse);
        result.AddParserForNullable<int>(int.TryParse);
        result.AddParserForNullable<short>(short.TryParse);
        return result;
    }

    /// <summary>
    /// Registers a parser for the given type T
    /// </summary>
    public void AddParser<T>(Parser<T> parser)
    {
        _parsersByType[typeof(T)] = (string input, out object output) => ParseObject(parser, input, out output);
    }

    /// <summary>
    /// Registers a parser for the given type T as well as <see cref="Nullable{T}"/>
    /// </summary>
    public void AddParserForNullable<T>(Parser<T> parser)
        where T : struct
    {
        _parsersByType[typeof(T)] = (string input, out object output) => ParseObject(parser, input, out output);
        _parsersByType[typeof(T?)] = (string input, out object output) => ParseNullable(parser, input, out output);
    }

    /// <summary>
    /// For nullable types, return null when the input is an empty string or null.
    /// </summary>
    /// <remarks>This is not called for the string-parser. Meaning an empty string is not parsed into a null value.</remarks>
    private bool ParseNullable<T>(Parser<T> parser, string input, out object output)
        where T : struct
    {
        bool result;
        if (string.IsNullOrEmpty(input))
        {
            result = true;
            output = null;
        }
        else
        {
            result = ParseObject(parser, input, out output);
        }
        return result;
    }

    /// <summary>
    /// Helper method to convert the typed output into an object (possibly boxing the value)
    /// </summary>
    private bool ParseObject<T>(Parser<T> parser, string input, out object output)
    {
        bool result = parser(input, out T typedOutput);
        output = typedOutput;
        return result;
    }

    /// <summary>
    /// Finds the parser for the given target type and uses it to parse the input.
    /// </summary>
    public object Parse<T>(string input)
    {
        Type targetType = typeof(T);
        object result;
        if (!_parsersByType.TryGetValue(targetType, out UntypedParser parser))
        {
            throw new ArgumentException($"No parser defined for type '{targetType}'.");
        }
        else if (!parser(input, out result))
        {
            throw new ArgumentException($"Cannot parse '{targetType}' from  input '{input}'.");
        }
        return result;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM