65.9K
CodeProject 正在变化。 阅读更多。
Home

通用类型转换器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (161投票s)

2011年9月1日

Ms-PL

19分钟阅读

viewsIcon

372446

downloadIcon

8134

.NET 中类型转换的瑞士军刀

目录

最新发布说明!

版本 2.7.1

版本 2.7.1 支持将 Guid 转换为 byte[]

NuGet 包 也已更新。查看支持的转换

版本 2.6.2

版本 2.6.2 支持 charReadOnlySpan 的常见转换。

版本 2.6

版本 2.6 支持新的框架类型 DateOnlyTimeOnly - 并添加了更多日期时间格式。

Notice

本文有点过时了——UniversalTypeConverter 已经完全重写。现在,请查看新的项目站点。但是你仍然可以继续阅读——大部分解释的概念也适用于新版本。

引言

本文讨论 .NET Framework 提供的不同类型转换可能性。最后,它在 UniversalTypeConverter 中提供了所有这些方法(以及更多)的组合,该转换器几乎可以将每种类型转换为另一种类型。

我是在需要将来自数据库的值映射到某些对象时遇到这个问题的。当然,这个问题可以通过O/R 映射器来解决。但这一次,对数据库的查询以及映射结果的对象只有在运行时才知道。这是一种报表工具。由于不确定确切的类型,我决定寻找一个通用的转换解决方案。但这些解决方案都没有为所有所需类型提供一个“现成”的方法,例如“将 x 转换为 y”。于是 UniversalTypeConverter 的想法就诞生了。

只是为了让你了解我在寻找什么

int myInt = myGivenValue.ConvertTo<int>();

无论 myGivenValue 的类型是什么。

如果您继续阅读,您将深入了解转换。不仅从编程角度,还描述了实际背景和原因。所以,给自己泡杯咖啡、茶、啤酒或任何你喜欢的东西,然后继续阅读。

如果您对整个故事不感兴趣,可以跳到最后,查看使用代码

一个解决方案

在我的研究中,我偶然发现了一些关于 TypeConverter 的好文章,我们很快就会仔细研究。在那之前,您可以将 TypeConverter 想象成一种通用的方式,告诉一种类型如何从/向另一种类型进行转换。这似乎是一个非常优雅的解决方案。所以我用一些不同的类型尝试了一下——一切都运行良好——并且认为我已经准备好完成这项工作了。多么天真...

设置项目

首先,我建立了一个我认为有用的所有类型转换组合的矩阵。您可以查看这个矩阵——它是在 Excel 中完成的,可以在解决方案下载中找到(在测试解决方案的 _Documents 文件夹下)。我试图涵盖所有基本类型(也称为“公共语言运行时类型”)、它们的可空对应物(例如 int?)以及 null 本身。这个矩阵是我在实际编程之前编写的单元测试的基础。如果您查看测试——不要惊慌:超过 1,500 个测试中的大部分是我编写的一个小程序构建的,而不是手动设置的。对于这种结构化测试,使用代码生成工具创建它们可能会更容易。矩阵中未提及——但有用——因此添加到测试中——我也期待支持 Enum。除了测试之外,我还设置了 UniversalTypeConverter 类,它包含了我期望的“现成”通用 ConvertTo<T> 方法。这样,所有生成的测试都运行了——当然,失败了。所以我已经准备好进行实际编程,目标是让一个接一个的测试闪耀绿色。

关于代码

如果您正要查看源代码,我将简要解释其结构:主方法是 TryConvert 最重载的版本。大多数其他 public 方法将其工作委托给此方法。当然,此方法也将其部分工作委托给私有助手。因为我实现了 Try 模式,所以大多数这些助手方法都必须遵循此概念,并在将转换后的值作为 out 参数处理时返回一个布尔值。

该代码使用了 Code ContractsFluentAssertions 进行测试。因此,您需要安装它们——两者都是免费的——才能编译和测试源代码。但不用担心,最终的 DLL 可以在 bin 文件夹中找到——随时可用。

从简单开始

一开始会检查一些简单的场景。您会在前面提到的 TryConvert 方法中找到代码。如果目标类型是 object,则无需执行任何操作——所有内容都是 object——因此我们只需返回它。如果输入类型可以分配给请求的目标类型,无论是通过实现请求的接口,还是通过继承请求的类型,或者仅仅是请求的类型本身,解决方案都同样简单。

看一下代码

if (destinationType == typeof(object)) {
    result = value;
    return true;
}
if (ValueRepresentsNull(value)) {
    return TryConvertFromNull(destinationType, out result, options);
}
if (destinationType.IsAssignableFrom(value.GetType())) {
    result = value;
    return true;
}

Null (空值)

您会注意到上面代码中对 null 的处理。ValueRepresentsNull 方法检查值是否为 nullDBNull.Value。在 ADO.NET 操作数据库的世界中,DBNull 代表“正常”的 null

我认为每个人在第一次(以及第二次)处理数据库时都会遇到这个问题。所以我以相同的方式处理这两个 null

如果某物为 null,我们需要处理它——就像在 TryConvertFromNull 中那样

result = GetDefaultValueOfType(destinationType);
if (result == null) {
    return true;
}
return (options & ConversionOptions.AllowDefaultValueIfNull) ==
       ConversionOptions.AllowDefaultValueIfNull;

GetDefaultValueOfType 方法返回给定类型的默认值

return type.IsValueType ? Activator.CreateInstance(type) : null;

这是可行的,因为每个 ValueType 都有一个无参数构造函数,所有其他类型都支持 null

如果默认值为 null,我们就完成了——因为转换后 null 仍然是 null

否则——也就是说对于所有 ValueType(例如,int 的默认值为 0)——只有在您明确接受非 null 默认值的情况下才可能。这可以通过 ConversionOption AllowDefaultValueIfNull 进行控制。一些 public 方法接受更多选项——AllowDefaultValueIfNull 是其中之一。其他选项将在稍后描述。现在,我认为有一个选项允许将 null 转换为非可空类型是很好的。

类型转换器

是时候介绍 TypeConverter 了。MSDN 这样描述它:“提供了一种统一的方式将值类型转换为其他类型”。显然,这就是我们所需要的一切!

主要思想是,您可以定义自己的 TypeConverter,并通过将 TypeConverterAttribute 设置到您的类上,将它们分配给您自己的类型。这样,每个人都可以通过简单地调用 TypeDescriptor.GetConverter 来获取合适的 TypeConverter。一旦知道了 TypeConverter,您就可以使用 CanConvertFromCanConvertToConvertFromConvertTo 方法进行验证和转换。这确实是一种通用的方式!如果您的请求类型受支持,您就完成了。

所以 private TryConvertByDefaultTypeConverters 方法的代码看起来非常简单

TypeConverter converter = TypeDescriptor.GetConverter(destinationType);
if (converter != null) {
    if (converter.CanConvertFrom(value.GetType())) {
        try {
            result = converter.ConvertFrom(null, culture, value);
            return true;
        }
        catch {
        }
    }
}
converter = TypeDescriptor.GetConverter(value);
if (converter != null) {
    if (converter.CanConvertTo(destinationType)) {
        try {
            result = converter.ConvertTo(null, culture, value, destinationType);
            return true;
        }
        catch {
        }
    }
}
return false;

您会问,为什么在调用 CanConvertFrom/To 已经检查了转换后还要使用 try-catch 呢?因为 CanConvertFrom/To 只检查转换的可能性!通常,将 string 转换为 DateTime 是可能的——但实际上它取决于 string 的值,例如,“Hello World”并不是真正的 DateTime,尝试转换它会抛出异常。如果 TypeConverter 实现了 Try-Pattern——例如 TryConvertFrom——那会很好,但遗憾的是事实并非如此。所以我们必须自己捕获这个可能的错误。

给定的文化用于全球化——我们稍后会讨论这个问题。

如果您有兴趣了解更多关于 TypeConverter 的信息——甚至想自己构建一个——您应该查看 CodeProject 上 Klaus Salchner 的文章

目前,.NET Framework 提供了许多针对不同类型的预定义 TypeConverter,并且这种技术被 序列化 大量使用——例如,将各种类型放入 ASPX 页面的 ViewState 中,而 ViewState 只包含一个 string。考虑到这一点,我开始了我的测试,期待着看到一切都闪耀着绿色...

IConvertible

但一些测试仍然失败了。例如,一个 int 不能转换为 decimal。我有点困惑,因为这些测试使用的是基本类型,我期望 .NET Framework 为每个基本类型提供一个合适的 TypeConverter——而不仅仅是其中一些!但这只是一个期望...

于是我打开了一个反编译器(例如 ILSpy),查看了 mscorlib 程序集内 Convert 类的不同 ConvertTo 方法。果然——我遇到了 IConvertible 接口。MSDN 这样描述这个接口:“定义将实现引用或值类型的值转换为具有等效值的公共语言运行时类型的方法”。别问我为什么——但很明显,框架实现了不止一种处理转换的方式……而且看起来很明显,我们也必须考虑它们。

因此,作为一种“回退”机制,如果没有 TypeConverter 可以完成这项工作,则将值传递给 TryConvertByIConvertibleImplementation 方法。它会检查给定值类型是否实现了 IConvertible。此接口为每个受支持的目标类型提供了一个显式的 ToX 方法。所以只有不太优雅的定义一些 if-then 块的方法。同样,没有 Try 模式。所以必须在 try-catch 中以“暴力破解”的方式完成。

总而言之,代码看起来像这样

if (value is IConvertible) {
    try {
        if (destinationType == typeof(Boolean)) {
            result = ((IConvertible)value).ToBoolean(formatProvider);
            return true;
        }
        if (destinationType == typeof(Byte)) {
            result = ((IConvertible)value).ToByte(formatProvider);
            return true;
        }
        if (destinationType == typeof(Char)) {
            result = ((IConvertible)value).ToChar(formatProvider);
            return true;
        }
        if (destinationType == typeof(DateTime)) {
            result = ((IConvertible)value).ToDateTime(formatProvider);
            return true;
        }
        if (destinationType == typeof(Decimal)) {
            result = ((IConvertible)value).ToDecimal(formatProvider);
            return true;
        }
        if (destinationType == typeof(Double)) {
            result = ((IConvertible)value).ToDouble(formatProvider);
            return true;
        }
        if (destinationType == typeof(Int16)) {
            result = ((IConvertible)value).ToInt16(formatProvider);
            return true;
        }
        if (destinationType == typeof(Int32)) {
            result = ((IConvertible)value).ToInt32(formatProvider);
            return true;
        }
        if (destinationType == typeof(Int64)) {
            result = ((IConvertible)value).ToInt64(formatProvider);
            return true;
        }
        if (destinationType == typeof(SByte)) {
            result = ((IConvertible)value).ToSByte(formatProvider);
            return true;
        }
        if (destinationType == typeof(Single)) {
            result = ((IConvertible)value).ToSingle(formatProvider);
            return true;
        }
        if (destinationType == typeof(UInt16)) {
            result = ((IConvertible)value).ToUInt16(formatProvider);
            return true;
        }
        if (destinationType == typeof(UInt32)) {
            result = ((IConvertible)value).ToUInt32(formatProvider);
            return true;
        }
        if (destinationType == typeof(UInt64)) {
            result = ((IConvertible)value).ToUInt64(formatProvider);
            return true;
        }
    }
    catch {
        return false;
    }
}
return false;

暂不理会 formatProvider——同样,它用于全球化,如稍后所述。

所以这应该能完成任务,不是吗?

没有异常就没有异常

错了!即使使用 IConvertible,例如 double 也不能转换为 char。这听起来可能很明显,因为你确实无法将 1.23 处理为 char。但你也不能将 400 (int) 处理为 char(因为超出范围);然而,.NET Framework 开箱即用地支持从 intchar 的转换。你明白我的意思吗?为什么不将 1.00 转换为 char?因此,在 TryConvertByIntermediateConversion 方法中,通过中间类型处理了一些特殊情况

if (value is char && (destinationType == typeof(double) || destinationType == typeof(float))) {
    return TryConvertCore(System.Convert.ToInt16(value), 
        destinationType, ref result, culture, options);
}
if ((value is double || value is float) && destinationType == typeof(char)) {
    return TryConvertCore(System.Convert.ToInt16(value), 
    destinationType, ref result, culture, options);
}
return false;

这里没什么好讨论的——它只是需要被发现。

隐式 - 但显式!

最初,通过中间类型转换覆盖了更多的案例。但在发布本文的第一个版本后,leppie 指出了隐式和显式转换运算符——谢谢 leppie!

那么隐式和显式转换是什么?

隐式转换是——嗯,隐式的——当你尝试从较小的整数类型转换为较大的整数类型,或者从派生类转换为基类时。
这就是为什么你可以写出如下代码的原因

int a = 123;
float b = a;

显式转换需要您使用一些特殊的语法,只是为了表明您知道自己在做什么。这是因为在转换过程中可能会丢失信息。例如,将数字类型转换为精度较低或范围较小的其他类型。这种特殊语法称为“强制转换”,您必须使用强制转换运算符——即将表达式放在括号中。如果您查看它,您一定会认出它

float a = 123;
int b = (int)a;

这一切都有效,因为为每种转换都定义了特殊的 static 方法。这些方法用 operator 关键字标记,因此有一个 implicit 和一个 explicit 运算符。编译器会将您的强制转换(如上述示例所示)定向到这些方法。这就是它如此流畅地融入语法的原因。也许这就是我没有将它们识别为转换方法的原因。

顺便说一下,这些 operator 方法也负责其他操作——例如,加法。

因此,如果我们要将它们用于 UniversalTypeConverter,就必须自行管理这些调用。但是去哪里找它们呢?我发现这些 operator 方法被编译为普通方法并添加到受影响的类型中。按照约定,这些方法总是命名为 “op_Implicit” 或 “op_Explicit”。您可以通过反编译器检查 mscorlib.dll 中的 Decimal 类型来查看这一点。

为了根据给定的输入和目标类型调用这些方法的正确版本,我们使用反射,就像在 TryConvertXPlicit 方法中那样

private static bool TryConvertXPlicit(object value, Type invokerType, 
    Type destinationType, string xPlicitMethodName, ref object result) {
    var methods = invokerType.GetMethods(BindingFlags.Public | BindingFlags.Static);
    foreach (MethodInfo method in methods.Where(m => m.Name == xPlicitMethodName)) {
        if (destinationType.IsAssignableFrom(method.ReturnType)) {
            var parameters = method.GetParameters();
            if (parameters.Count() == 1 && 
                parameters[0].ParameterType == value.GetType()) {
                try {
                    result = method.Invoke
                        (null, new[] { value });
                    return true;
                }
                // ReSharper disable EmptyGeneralCatchClause
                catch {
                    // ReSharper restore EmptyGeneralCatchClause
                }
            }
        }
    }
    return false;
}

此方法检查每个具有给定 operatorMethodName(“op_Explicit”或“op_Implicit”)的方法是否具有所需的类型,并在找到时调用适当的方法。它从 TryConvertXPlicit 调用。一次用于 value 的类型,一次用于 destinationType。这是因为我们不知道哪个类型定义了适当的 operator 方法。请看代码

private static bool TryConvertXPlicit(object value, Type destinationType, 
    string operatorMethodName, ref object result) {
    if (TryConvertXPlicit(value, value.GetType(), 
        destinationType, operatorMethodName, ref result)) {
        return true;
    }
    if (TryConvertXPlicit(value, destinationType, 
        destinationType, operatorMethodName, ref result)) {
        return true;
    }
    return false;
}

因此,这为我们提供了 UniversalTypeConverter 涵盖的另一种通用转换方式。

Enums

这时,我不再相信存在一致的转换方式了。我也没失望。  

尝试使用目前为止描述的方法将 int 转换为 Enum。它不起作用——您必须使用 Enumstatic 方法 ToObject,就像在 TryConvertToEnum 方法中那样

try {
    result = Enum.ToObject(destinationType, value);
    return true;
}
catch {
    return false;
}

可空类型

又提到 Null?我知道我们已经谈过 null 了。但是还记得 ValueType(如 int)不能为 null 的问题吗?自 .NET 2.0 版以来,有一种包装类型为 ValueType 提供了某种 null 赋值。同样,我带着数据库背景来思考——数据库能够在 int 列中存储 null。如果你必须在 .NET 侧存储这些 null——或其他 null——你应该仔细看看所谓的可空类型。可空类型。我也是如此。达到这一点,我对到目前为止描述的任何方法都没有处理这些类型的转换并不感到惊讶。

如上所述,可空类型是实际类型的包装器。所以我想在转换前“解包”实际类型。这样,我们就可以按原样使用所有描述的方法。幸运的是,如果需要,回到可空类型是由框架隐式完成的——所以不需要做更多的事情。

所以你可以看到,转换的核心类型是在 TryConvert 中定义的

Type coreDestinationType = IsGenericNullable(destinationType) ? 
GetUnderlyingType(destinationType) : destinationType;

辅助方法检查是否使用了可空类型,如果是,则获取其基础类型。

IsGenericNullable
return type.IsGenericType &&
    type.GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition();
GetUnderlyingType
return Nullable.GetUnderlyingType(type);

从这里开始,一切都像它是一个“正常”的值类型一样进行。

别害怕,如果你不能像理解其他部分那样轻松理解本节。如果你之前没有听说过泛型和可空类型,这并不容易。如果你感兴趣,你可以自己学习这些东西,然后再次阅读本节。但现在,请继续阅读本文。  

完成

信不信由你,所有预期的转换都奏效了——万岁!所以我们可以看看在转换过程中需要注意的一些一般性事项,以及 UniversalTypeConverter 如何在这方面为您提供支持。

放眼全球

string 转换为另一种类型通常取决于给定的 culture。例如,您可以将 1,000 读作一千。但在德国,它表示小数点后有三位数字的一,因为这里的小数点是逗号——相当令人困惑,不是吗?因此,转换(例如转换为 decimal)将返回不同的结果。因此,每个方法都有一个重载,您可以在其中指定 culture。这就是为什么我们在辅助方法中使用 culture 或 formatProvider,如上所述。除非另有指定,否则默认使用 当前 culture。如果您要进行国际化,您确实应该注意为数字和 DateTime 转换定义正确的 culture——这是一个隐蔽的错误,因为转换是在没有异常的情况下完成的,但结果却是错误的!

使用代码

只需引用 DLL 并将其与您的最终二进制文件一起分发。UniversalTypeConverter 作为 TB.ComponentModel 命名空间中的 static 类型提供。因此,您无需创建它的实例即可使用它

decimal myValue = UniversalTypeConverter.ConvertTo<decimal>(myStringValue);

如果您不确定该类型是否可以转换为另一种类型,可以使用 TryConvertTo 方法

decimal result;
bool canConvert = UniversalTypeConverter.TryConvertTo<decimal>(myStringValue, out result);

它遵循 Try 模式,因此如果类型被转换,它将返回 true,您可以从 out 参数中读取转换后的值。如果类型未被转换,它将返回 false。此时您应该忽略 out 参数。

如果您只想检查类型是否可转换——而不关心结果——您可以使用 CanConvertTo 方法,无需定义 out 参数

bool canConvert = UniversalTypeConverter.CanConvertTo<decimal>(myStringValue);

如果您在编译时知道类型,所有这些泛型方法都能很好地工作。如果您在运行时获取类型,则必须使用非泛型版本——将目标类型作为参数传递

... = UniversalTypeConverter.Convert(myStringValue, requestedType);

扩展方法

此外,UniversalTypeConverter 附带了一组用于 object 类型的扩展方法。这样,您可以更方便地使用它

decimal myValue = myStringValue.ConvertTo<decimal>(CultureInfo.CurrentCulture);

选项

实践表明,有些选项您在需要时必须自行指定。在放眼全球中,我已经提到 culture 是一个选项。其他选项由 ConversionOptions 枚举指定。此枚举定义为标志,因此您可以根据需要组合这些选项

  • AllowDefaultValueIfNull:如果给定值为 null 且目标类型不支持 null(如Null部分所述),则返回给定目标类型的默认值。
  • AllowDefaultValueIfWhitespace:如果给定值为仅包含空白的 string,但不支持从空白进行转换,则返回给定目标类型的默认值。
  • EnhancedTypicalValues:允许从对我来说有意义的典型值进行转换。例如,将 string "True" 转换为 bool 可以直接工作。但我决定 "Yes"、"No" 等也可以转换。您可以查看 TryConvertSpecialValues 方法来了解这些 EnhancedTypicalValues

除非另有说明,否则转换将使用以下设置完成

  • CultureInfo 取自 CultureInfo.CurrentCulture
  • ConversionOptions 默认使用 EnhancedTypicalValues

NuGet 包

UniversalTypeConverter 也提供 NuGet 包。只需在 NuGet 包管理器中搜索 UniversalTypeConverter 即可。

.net Standard/Core

最新的 NuGet 包支持 .net Standard >= 1.3,因此可以在 .net Core 中使用。
感谢 Stefan Ossendorf 为此付出的努力!

IEnumerable - 数组、列表、集合等等

使用 UniversalTypeConverter 表明它可以扩展以简化与数组或列表的工作。为了保持通用性,我们将普遍讨论 IEnumerable。因此,现在包含三个新功能

  1. IEnumerable 的所有元素转换为指定类型(例如,将 int[] 转换为 string[])。
  2. IEnumerable 的所有元素转换为字符串表示形式(例如,“1,2,3”)。
  3. 拆分字符串(例如,“1,2,3”)并将所有子字符串转换为指定类型的 IEnumerable

使用 IEnumerableIEnumerable<T> 让您能够方便地与 Linq 交互。因此,我实现了一个精巧的流式接口来配置转换——只是为了与 Linq 保持一致。

我将为每个新功能提供一个带注释的示例。大多数选项都是——嗯,可选的。我将向您展示最大程度的配置。您可以根据需要进行裁剪。

1. 从 IEnumerable 到 ConvertToEnumerable<T>()

string[] sourceValues = new[] { "12", null, "118", "xyz" };
int[] convertedValues = sourceValues.ConvertToEnumerable<int>()
    // null values in the input are ignored.
    .IgnoringNullElements()
    // Values which are not convertible to the destination type are ignored.
    .IgnoringNonConvertibleElements()
    // Specifies the culture to use - have a look above in this article.
    .UsingCulture(CultureInfo.CurrentCulture)
    // Specifies the options to use - have a look above in this article.
    .UsingConversionOptions(ConversionOptions.AllowDefaultValueIfWhitespace)
    // You can continue with basic Linq:
    .ToArray();

这将生成一个包含两个元素(12 和 118)的 int 数组。

2. ConvertToStringRepresentation()

object[] input = new object[] { 2, null, true, "Hello world!", ""};
    string stringRepresentation = input
    .ConvertToStringRepresentation(
        // Specifies the culture to use - have a look above in this article.
        CultureInfo.CurrentCulture,
        // Specified an IStringConcatenator which uses the semicolon
        // as seperator, shows null values as "null" and ignores empty values.
        new GenericStringConcatenator(";", ".null.", ConcatenationOptions.IgnoreEmpty)
    );

这将返回字符串 "2;.null.;True;Hello world!"。

您可以默认忽略 IStringConcatenator。为了简化分隔符和空值的定义,有重载方法。默认情况下,它使用分号和 ".null."。但是可能会出现您想指定更多关于如何构建结果字符串的细节的情况。那时您可以创建自己的 IStringConcatenator 并传递给转换方法。如果您愿意,可以研究一下代码。

3. 从字符串到 ConvertToEnumerable<T>()

这是 ConvertToStringRepresentation() 的配套方法,看起来像这样

string input = "1;;.null.;3;xyz";
int[] result = input.ConvertToEnumerable<int>(new GenericStringSplitter(";"))
// Specifies an IStringSplitter which splits the input by semicolon.

    // Trims the end of each splitted element before conversion.
    .TrimmingEndOfElements()
    // Trims the start of each splitted element before conversion.
    .TrimmingStartOfElements()
    // Values which are empty are ignored.
    .IgnoringEmptyElements()
    // Specifies how null values are represented within the unput string.
    .WithNullBeing(".null.")
    // Values which are null are ignored. 
    .IgnoringNullElements()
    // Values which are not convertible to the destination type are ignored. 
    .IgnoringNonConvertibleElements()
    // Specifies the options to use - have a look above in this article.
    .UsingConversionOptions(ConversionOptions.AllowDefaultValueIfWhitespace)
    // Specifies the culture to use - have a look above in this article.
    .UsingCulture(CultureInfo.CurrentCulture)
    // You can continue with basic Linq:
    .ToArray();

这将生成一个包含两个元素(1 和 3)的 int 数组。

同样,IStringSplitter 用于更复杂的场景。您可以创建自己的类以支持所需的格式(例如,拆分 CSV 行)。

处理 IEnumerable 时,您可以调用 Try() 而不是 ToArray()。如果转换成功,它将返回 true,并将结果作为 out 参数处理。

就是这样——感谢您的持续阅读。顺便说一句——NuGet 包也更新了。所以别忘了也更新一下!

限制

转换仅在所使用的类型提供所述接口(即继承自 TypeConverter、实现 IConvertible 或提供适当的 operator 方法)时才有效。但 TypeConverter 背后的概念应该适用于所有您自己的类型。更不用说,类型在某种程度上应该是兼容的——将 TextBox 转换为 bool 当然不会成功。

我经常提到我实现了 Try 模式。如果您深入研究,您会读到它应该在不使用 try-catch 的情况下实现。这是因为性能原因。但是您会看到我将转换包装在 try-catch 中。这是因为 TypeConverterIConvertible 缺乏合适的 TryConvert

结论

.NET 不提供跨所有类型的通用转换方式。即使是基本类型也未以相同的方式处理。并且并非所有跨基本类型的可能转换都受支持。我们研究了不同的技术,最终得到了 UniversalTypeConverter 作为一种通用解决方案,填补了空白并提供了一些有用的额外选项。可能是历史原因导致 TypeConverter 未用于所有类型。但这绝对是如果您想提供自己的可转换类型的方式。

总而言之,我希望您觉得这篇文章有用。如果觉得有用,请为它投票。  

如果您遇到 UniversalTypeConverter 不支持的转换——当然,除了 TextBoxbool 或类似的东西——请随时给我留言。

历史

  • 2024 年 7 月 26 日:提到新版本(2.7.1 版)
  • 2024 年 7 月 8 日:提到新版本(2.6.2 版)
  • 2022 年 8 月 29 日:提到新版本(2.6 版)
  • 2019 年 4 月 25 日:提到新版本(2 版)
  • 2017 年 4 月 23 日:支持 .net Standard/Core
  • 2013 年 6 月 5 日:NuGet 链接更新
  • 2012 年 9 月 20 日:修复了损坏的链接 
  • 2012 年 7 月 15 日:帮助文件已更新
  • 2012 年 7 月 8 日:添加了对 IEnumerable 的支持,源代码已更新
  • 2012 年 2 月 26 日:帮助文件已添加到下载部分
  • 2011 年 9 月 25 日:添加了 NuGet 包部分;对文本进行了一些小修改;源代码已更新
  • 2011 年 9 月 10 日:添加了 隐式 - 但显式! 部分;对文本进行了一些小修改;源代码已更新
  • 2011 年 9 月 1 日:第一个版本
© . All rights reserved.