.NET: Y at-il une forme de String.Format pour insérer la valeur d'une propriété d'objet dans une chaîne?

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

  •  21-08-2019
  •  | 
  •  

Question

Je pense que la réponse directe à la question est « Non », mais je suis en espérant que quelqu'un a écrit une véritable bibliothèque simple à faire (ou je peux le faire ... ugh ...)

Permettez-moi de démontrer ce que je cherche un exemple. Supposons que j'ai eu le suivant:

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

Je voudrais pouvoir faire quelque chose comme ceci:

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);
}

Quelqu'un sait s'il y a un format pour faire quelque chose comme ça ou si quelqu'un a écrit une bibliothèque pour le faire? Je sais que ce ne devrait pas être trop dur, mais je préfère ne pas être Réimplémenter la roue.

Était-ce utile?

La solution

Edit: Vous ne devez pas mettre en œuvre IFormattable pour chaque objet ... ce serait un PITA, ce qui limite sévèrement, et une charge d'entretien assez important. Il suffit d'utiliser la réflexion et un IFormatProvider avec ICustomFormatter et ça va travailler avec any objet. String.Format a une surcharge de prendre une en tant que paramètre.

Je ne l'ai jamais pensé auparavant, mais tu me intriguée - donc je devais lui donner un tourbillon rapide. Notez que je choisi pour permettre à une chaîne de format supplémentaire à passer à la valeur de la propriété, et qu'elle fonctionne uniquement avec des propriétés non indexées et accessibles (bien que vous pouvez facilement ajouter que).

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);
    }
}

Et votre exemple légèrement modifié:

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);

Sorties:

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.

Autres conseils

Vous pouvez remplacer le ToString () pour votre classe.

Bon article

Ce qui est après le « : » est passé comme argument à la méthode ToString de votre classe
. Il suffit de déclarer une méthode ToString accepter une chaîne, et la etc. « Nom », 'NumberOfCats sera de passer dans ce paramètre.

EDIT: Vous devez implémenter System.IFormattable. Cela fonctionne:

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));
    }
}

Consultez ma bibliothèque "expansif"

Nuget ici: http://nuget.org/List/Packages/Expansive

GitHub ici: http://github.com/anderly/expansive

Je ne vois vraiment pas comment cela:

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

est mieux que cela:

"{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);

En fait, depuis que vous perdez IntelliSense aide dans le premier, non seulement il est plus enclin à l'échec, mais prendrait probablement plus de temps à écrire dans un IDE.

Et si vous voulez faire quelque chose comme ça, vous pouvez toujours concocter une méthode d'extension pour elle. Je parie que ce serait ressembler à un cauchemar, tho.

Boo ou Nemerle a quelque chose comme ça. J'ai essayé de penser à une façon simple et agréable de le faire pour quelques années, sans réponse facile.

Le mieux que vous pouvez faire est que vous fournissez vos propres IFormatProvider et analyser l'entrée manuellement (la partie merdique ...).

Si vous décidez d'analyser la chaîne de format vous-même, vous devriez considérer ceci ...:

Le projet Spring.NET a Spring.NET Langage d'expression dans Spring.Core. Il vous permet d'interroger un graphe d'objet en pointant les propriétés en utilisant des chaînes. En utilisant votre exemple, vous pouvez imaginer quelque chose comme ceci:

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"));

(je ne serais probablement pas stocker un e-mail comme ça, mais je ne pouvais pas trouver quelque chose de mieux)

Ceci est quelque chose que beaucoup font dans le monde Python en utilisant « les habitants de SomeString% de (). » Qu'est-ce que vous proposez a bien quelques pauses fondamentales de la façon dont fonctionne String.Format:

  • normalement la notation de l'espace réservé a le formatage de chaîne d'informations après les deux points, alors que vous voulez faire accès à la propriété.

  • les indices d'espace réservé ({0, {1, etc.) se réfèrent normalement aux arguments de numberes dans les params args, mais il semble que vous voulez que votre fonction pour rendre la chaîne entière pour chaque paramètre qui est passé dans. Faut-il concaténer? retourné comme un tableau de chaînes?

Vous pouvez finir par écrire celui-ci vous, dans ce cas, vous pouvez ignorer la notation d'index entièrement (donc {NumberOfCats} au lieu de {0: NumberOfCats} ou même utiliser le nom de la propriété suivie par le fournisseur de format {NumberOfCats: c}. consommer les métadonnées d'un objet d'entrée ne doit pas être trop difficile.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top