我现在意识到您实际上是在要求可以与 String.Format() 一起使用的东西-我想我应该在发布之前阅读该问题两次;-)
我不喜欢每次都必须显式传入格式提供程序的解决方案——据我从本文中收集到的信息,解决此问题的最佳方法是实现 FileSize 类型,实现 IFormattable 接口。
我继续实现了一个支持此接口的结构,并且可以从整数转换。在我自己的文件相关 API 中,我将让我的 .FileSize 属性返回一个 FileSize 实例。
这是代码:
using System.Globalization;
public struct FileSize : IFormattable
{
private ulong _value;
private const int DEFAULT_PRECISION = 2;
private static IList<string> Units;
static FileSize()
{
Units = new List<string>(){
"B", "KB", "MB", "GB", "TB"
};
}
public FileSize(ulong value)
{
_value = value;
}
public static explicit operator FileSize(ulong value)
{
return new FileSize(value);
}
override public string ToString()
{
return ToString(null, null);
}
public string ToString(string format)
{
return ToString(format, null);
}
public string ToString(string format, IFormatProvider formatProvider)
{
int precision;
if (String.IsNullOrEmpty(format))
return ToString(DEFAULT_PRECISION);
else if (int.TryParse(format, out precision))
return ToString(precision);
else
return _value.ToString(format, formatProvider);
}
/// <summary>
/// Formats the FileSize using the given number of decimals.
/// </summary>
public string ToString(int precision)
{
double pow = Math.Floor((_value > 0 ? Math.Log(_value) : 0) / Math.Log(1024));
pow = Math.Min(pow, Units.Count - 1);
double value = (double)_value / Math.Pow(1024, pow);
return value.ToString(pow == 0 ? "F0" : "F" + precision.ToString()) + " " + Units[(int)pow];
}
}
还有一个简单的单元测试演示了它是如何工作的:
[Test]
public void CanUseFileSizeFormatProvider()
{
Assert.AreEqual(String.Format("{0}", (FileSize)128), "128 B");
Assert.AreEqual(String.Format("{0}", (FileSize)1024), "1.00 KB");
Assert.AreEqual(String.Format("{0:0}", (FileSize)10240), "10 KB");
Assert.AreEqual(String.Format("{0:1}", (FileSize)102400), "100.0 KB");
Assert.AreEqual(String.Format("{0}", (FileSize)1048576), "1.00 MB");
Assert.AreEqual(String.Format("{0:D}", (FileSize)123456), "123456");
// You can also manually invoke ToString(), optionally with the precision specified as an integer:
Assert.AreEqual(((FileSize)111111).ToString(2), "108.51 KB");
}
如您所见,现在可以正确格式化 FileSize 类型,还可以指定小数位数,以及在需要时应用常规数字格式。
我想您可以更进一步,例如允许显式格式选择,例如“{0:KB}”以强制以千字节为单位进行格式化。但我将把它留在这里。
我还将在下面留下我的初始帖子,因为这两个不喜欢使用格式化 API ...
给猫剥皮的 100 种方法,但这是我的方法 - 为 int 类型添加扩展方法:
public static class IntToBytesExtension
{
private const int PRECISION = 2;
private static IList<string> Units;
static IntToBytesExtension()
{
Units = new List<string>(){
"B", "KB", "MB", "GB", "TB"
};
}
/// <summary>
/// Formats the value as a filesize in bytes (KB, MB, etc.)
/// </summary>
/// <param name="bytes">This value.</param>
/// <returns>Filesize and quantifier formatted as a string.</returns>
public static string ToBytes(this int bytes)
{
double pow = Math.Floor((bytes>0 ? Math.Log(bytes) : 0) / Math.Log(1024));
pow = Math.Min(pow, Units.Count-1);
double value = (double)bytes / Math.Pow(1024, pow);
return value.ToString(pow==0 ? "F0" : "F" + PRECISION.ToString()) + " " + Units[(int)pow];
}
}
在您的程序集中使用此扩展名,要格式化文件大小,只需使用类似 (1234567).ToBytes() 的语句
以下 MbUnit 测试准确地阐明了输出的样子:
[Test]
public void CanFormatFileSizes()
{
Assert.AreEqual("128 B", (128).ToBytes());
Assert.AreEqual("1.00 KB", (1024).ToBytes());
Assert.AreEqual("10.00 KB", (10240).ToBytes());
Assert.AreEqual("100.00 KB", (102400).ToBytes());
Assert.AreEqual("1.00 MB", (1048576).ToBytes());
}
而且您可以轻松地将单位和精度更改为适合您需要的任何内容:-)