。网:是否有 String.Format 形式用于将对象属性的值插入字符串中?

StackOverflow https://stackoverflow.com/questions/357447

  •  21-08-2019
  •  | 
  •  

我认为这个问题的直接答案是“不”,但我希望有人编写了一个真正简单的库来做到这一点(或者我可以做到......呃......)

让我用一个例子来证明我正在寻找什么。假设我有以下内容:

class Person {
  string Name {get; set;}
  int NumberOfCats {get; set;}
  DateTime TimeTheyWillDie {get; set;}
}

我希望能够做这样的事情:

static void Main() {
  var p1 = new Person() {Name="John", NumberOfCats=0, TimeTheyWillDie=DateTime.Today};
  var p2 = new Person() {Name="Mary", NumberOfCats=50, TimeTheyWIllDie=DateTime.Max};

  var str = String.Format(

"{0:Name} has {0:NumberOfCats} cats and {1:Name} has {1:NumberOfCats} cats.  They will die {0:TimeTheyWillDie} and {1:TimeTheyWillDie} respectively
", p1, p2);

  Console.WriteLine(str);
}

有谁知道是否有一种格式可以做这样的事情,或者是否有人编写了一个库来做到这一点?我知道这应该不会太难,但我不想重新实现轮子。

有帮助吗?

解决方案

编辑:您没有实现IFormattable为每个对象......这会是一个皮塔饼,严重限制,以及一个相当大的维护负担。只要使用的反思与ICustomFormatter一个的IFormatProvider,它会用的任何的对象工作。的String.Format具有过载采取一个作为参数。

我从来没有想过这之前,但你吸引了我 - 所以我不得不给它一个快速旋转。请注意,我选择以允许附加格式字符串,并传递到属性值,而且它只有与非索引和访问性工作(尽管你可以很容易地添加)。

public class ReflectionFormatProvider : IFormatProvider, ICustomFormatter {
    public object GetFormat(Type formatType) {
        return formatType == typeof(ICustomFormatter) ? this : null;
    }

    public string Format(string format, object arg, IFormatProvider formatProvider) {
        string[] formats = (format ?? string.Empty).Split(new char[] { ':' }, 2);
        string propertyName = formats[0].TrimEnd('}');
        string suffix = formats[0].Substring(propertyName.Length);
        string propertyFormat = formats.Length > 1 ? formats[1] : null;

        PropertyInfo pi = arg.GetType().GetProperty(propertyName);
        if (pi == null || pi.GetGetMethod() == null) {
            // Pass thru
            return (arg is IFormattable) ? 
                ((IFormattable)arg).ToString(format, formatProvider) 
                : arg.ToString();
        }

        object value = pi.GetGetMethod().Invoke(arg, null);
        return (propertyFormat == null) ? 
            (value ?? string.Empty).ToString() + suffix
            : string.Format("{0:" + propertyFormat + "}", value);
    }
}

和您的稍微修改的示例:

var p1 = new Person() {Name="John", NumberOfCats=0, TimeTheyWillDie=DateTime.Today};
var p2 = new Person() {Name="Mary", NumberOfCats=50, TimeTheyWillDie=DateTime.MaxValue};

var str = string.Format(
    new ReflectionFormatProvider(),
    @"{0:Name} has {0:NumberOfCats} cats and {1:Name} has {1:NumberOfCats} cats. 
    They will die {0:TimeTheyWillDie:MM/dd/yyyy} and {1:TimeTheyWillDie} respectively.
    This is a currency: {2:c2}.", 
    p1, 
    p2,
    8.50M
);

Console.WriteLine(str);

输出:

John has 0 cats and Mary has 50 cats. 
They will die 12/10/2008 and 12/31/9999 11:59:59 PM respectively.
This is a currency: $8.50.

其他提示

您可以覆盖您的类的 ToString() 。

好文章 这里

后是什么“:”作为参数传递到类的ToString方法通过点击。 只是声明了一个ToString方法接受一个字符串,“名称”,“NumberOfCats”等将在参数传递。

编辑:您必须实现System.IFormattable。工作的:

class Person : IFormattable
{
    public override string ToString()
    {
        return "Person";
    }

    public string ToString(string format, IFormatProvider formatProvider)
    {
        if (format == "Name")
        {
            return "John";
        }
        if (format == "NumberOfCats")
        {
            return "12";
        }
        return "Unknown format string";
    }

}

class Program
{
    static void Main(string[] args)
    {
        Person p = new Person();
        Console.WriteLine(string.Format("Name = {0:Name}",p));
        Console.WriteLine(string.Format("NumberOfCats = {0:NumberOfCats}", p));
    }
}

看看我的图书馆 “膨胀”

在这里的NuGet: http://nuget.org/List/Packages/Expansive

在这里的GitHub: http://github.com/anderly/expansive

我真不明白这是如何:

"{0:Name} has {0:NumberOfCats} cats and {1:Name} has {1:NumberOfCats} cats.  They will die {0:TimeTheyWillDie} and {1:TimeTheyWillDie} respectively", p1, p2);

是任何比这更好:

"{0} has {1} cats and {2} has {3} cats.  They will die {4} and {5} respectively
", p1.Name, p1.NumberOfCats, p2.Name, p2.NumberOfCats, p1.TimeTheyWillDie, p2.TimeTheyWillDie);

事实上,因为你在第一个失去智能感知的帮助,它不仅是更容易失败,但可能会需要更长的时间在IDE编写。

如果你想要做这样的事情,你总是可以掀起的扩展方法吧。我敢打赌,它看起来就像一场噩梦,寿。

嘘声或Nemerle具有这样的事情。我曾尝试现在想一个不错的简单的方法来做到这一点了几年,没有简单的答案。

你能做的最好的是提供你自己的IFormatProvider,并解析输入手动(蹩脚部分...)。

如果你决定自己解析格式字符串,你应该考虑这个...:

在Spring.NET项目具有 Spring.NET表达式语言在Spring.Core。它可以让你通过指向使用字符串的属性查询的对象图。使用你的榜样,你能想象这样的事情:

var person = new Person { Name = "joe", Email = new Email { Address = "joe@joe.com" } };

var message = string.Format("{0}'s e-mail is {1}",
    ExpressionEvaluator.GetValue(person, "Name"), 
    ExpressionEvaluator.GetValue(person, "Email.Address"));

(我可能不会存储电子邮件这样,但我不能想出更好的东西)

这是许多人通过使用“ somestring%locals()”在Python世界中所做的。您的建议虽然与字符串的工作方式有几个基本休息:Format的工作方式:

  • 通常,占位符表示法在冒号后面有字符串格式信息,而您想要进行属性访问。

  • 占位符索引({0、{1 等)通常引用 params args 中的 numberes 参数,但您似乎希望函数为传入的每个参数呈现整个字符串。它们应该连接起来吗?作为字符串数组返回?

因此,您最终可能会自己编写这个,在这种情况下,您可以完全跳过索引符号(因此使用 {NumberOfCats} 而不是 {0:NumberOfCats},甚至使用属性名称后跟格式提供程序 {NumberOfCats:c}。使用来自输入对象的元数据应该不会太困难。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top