Visual Studio 2015和IFormatProvider中的字符串插值(CA1305)

33

Visual Studio 2015中的新字符串插值样式如下:

Dim s = $"Hello {name}"

但是如果我使用这个,代码分析会告诉我我违反了CA1305:指定 IFormatProvider

以前我是这样做的:

Dim s = String.Format(Globalization.CultureInfo.InvariantCulture, "Hello {0}", name)

但是如何在新样式下完成呢?

我必须提到,我正在寻找一个针对 .Net 4.5.2 的解决方案(针对 .Net 4.6,dcastro 给出了答案)。


6
有人在整整七年半之前就预言了这一点。 - BoltClock
@BoltClock 他们通过允许您存储合并格式来解决了这个问题。 - jessehouwing
4个回答

22

您可以使用System.FormattableStringSystem.IFormattable类:

IFormattable ifs = (IFormattable)$"Hello, {name}";
System.FormattableString fss = $"Hello, {name}";

// pass null to use the format as it was used upon initialization above.
string ifresult = ifs.ToString(null, CultureInfo.InvariantCulture);
string fsresult = fss.ToString(CultureInfo.InvariantCulture);

您需要编译针对Framework 4.6的版本,因为 IFormattableFormattableString 这两个类在旧版本中不存在。所以,如果您的目标是更早的 .NET framework 版本,则无法使用插值语法而不触发错误。

除非您使用一个小技巧 (改编自Jon Skeet的gist,并编译针对4.6 RTM,以及从我的帐户派生出去的 )。只需将一个包含以下内容的类文件添加到您的项目中:

Update

There is now also a Nuget package available that will provide the same functionality to your project (thanks for bringing this to my attention @habakuk).

install-package StringInterpolationBridge

如果您不想在产品中添加额外的程序集,但又希望实现相同的功能,请将以下代码添加到您的项目中:

namespace System.Runtime.CompilerServices
{
    internal class FormattableStringFactory
    {
        public static FormattableString Create(string messageFormat, params object[] args)
        {
            return new FormattableString(messageFormat, args);
        }
    }
}

namespace System
{
    internal class FormattableString : IFormattable
    {
        private readonly string messageFormat;
        private readonly object[] args;

        public FormattableString(string messageFormat, object[] args)
        {
            this.messageFormat = messageFormat;
            this.args = args;
        }

        public override string ToString()
        {
            return string.Format(messageFormat, args);
        }

        public string ToString(string format, IFormatProvider formatProvider)
        {
            return string.Format(formatProvider, format ?? messageFormat, args);
        }

        public string ToString(IFormatProvider formatProvider)
        {
            return string.Format(formatProvider, messageFormat, args);
        }
    }
}

请参考:


这个实现与.NET 4.6附带的实现略有不同。特别是.NET忽略了IFormattable.ToString(string,IFormatProvider)上的格式参数。直接从CLR复制源代码可能更容易,就像StringInterpolationBridge一样:https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/FormattableString.cshttps://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/Runtime/CompilerServices/FormattableStringFactory.cs - Austin
3
如果您正在使用.NET 4.6或StringInterpolationBridge,有一个辅助方法可以用来创建Invariant culture字符串:FormattableString.Invariant。详情请参考:https://msdn.microsoft.com/en-us/library/system.formattablestring.invariant。 - Austin

12

如果你的目标是 .NET Framework 4.6,那么你可以利用字符串插值隐式地转换为 FormattableString 的事实:

引自 Thomas Levesque 的文章《在C# 6中自定义字符串插值》

这个特性不太为人所知的一个方面是,根据上下文,插入的字符串既可以被视为 String,也可以被视为 IFormattable

static string Invariant(FormattableString formattable)
{
    return formattable.ToString(CultureInfo.InvariantCulture);
}

string text = Invariant($"{p.Name} was born on {p.DateOfBirth:D}");

谢谢,但是我还在使用4.5.2版本(因为http://blogs.msdn.com/b/dotnet/archive/2015/07/28/ryujit-bug-advisory-in-the-net-framework-4-6.aspx - 但现在可能已经修复了)。是否也有一个适用于4.5版本的解决方案? - habakuk
@habakuk 不,我不这么认为。 - dcastro
请注意,导致您仍然停留在4.5.2版本的错误已经修复。运行Windows更新来解决此问题。 - jessehouwing
1
@habakuk 如果您阅读了链接的Levesque文章,似乎确实有这样一个方法!请查看[Jon Skeet的方法](https://gist.github.com/jskeet/9d297d0dc013d7a557ee) - default.kramer
@default.kramer,那个代码片段不幸地已经失效了,但修复它并不难。请看我的回答。 - jessehouwing

10
微软已经让使用字符串插值和符合CA1305: 指定 IFormatProvider更加容易。
如果你正在使用C# 6或更新版本,你可以使用using static指令。
此外,静态方法 FormattableString.Invariant 可用于 .NET Standard 1.3, .NET Core 1.0.NET Framework 4.6 及其之后版本。将这两者结合起来可以实现以下效果:
using static System.FormattableString;

string name = Invariant($"Hello {name}");

然而,如果你的目标是通过当前文化来完成插值,则在 .NET Core 3.0(目前是Preview 5)中提供了一个伴随静态方法FormattableString.CurrentCulture

using static System.FormattableString;

string name = CurrentCulture($"Hello {name}");

4

1
希望您不介意我将这个宝石添加到已经接受的答案中,以便完整。感谢您让我注意到它! - jessehouwing

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接