Строковая интерполяция: как заставить эту функцию работать с любым типом - PullRequest
0 голосов
/ 09 февраля 2020

Это функция для работы со списками при интерполяции строк. Он принимает List и внутреннее Fun c и добавляет строковый результат внутреннего Fun c, вызываемого для каждого члена списка, с разделителем.

Таким образом, следующее строит правильное начало оператора вставки ...

static void Main(string[] args)
{
    var tableName = "customers";
    var cols = new List<dynamic>
    {
        new { Name = "surname"},
        new { Name = "firstname"},
        new { Name = "dateOfBirth"}
    };
    Func<List<dynamic>, Func<dynamic, string>, string, string> ForEach = (list, func, separator) =>
        {
            var bldr = new StringBuilder();
            var first = true;
            foreach (var obj in list)
            {
                if (!first)
                    bldr.Append(separator);
                first = false;
                bldr.Append(func(obj));
            }
            return bldr.ToString();
        };

    var InsertStatement = $"Insert into { tableName } ( {ForEach(cols, col => col.Name, ", ")} )";
    Console.WriteLine(InsertStatement);
    Console.ReadLine();
}

Outputs ...

Insert into customers ( surname, firstname, dateOfBirth )

Это работает для Dynami c. Как мне заставить это работать для любого типа? Внешний Fun c не должен заботиться о типе в списке, он просто передает его внутреннему Fun c.

Ответы [ 3 ]

0 голосов
/ 09 февраля 2020

. NET фреймворк уже предоставляет вам универсальную c функцию для достижения того, что вы пытаетесь сделать String.Join , и вы можете комбинировать ее с оператором LINQ Select, который позволит вам использовать лямбда-тип c для выбора свойства, которое вы хотите напечатать. Вы можете просмотреть исходный код этих методов, если вам это интересно, так как они с открытым исходным кодом.

using System;
using System.Collections.Generic;
using System.Linq;

public class MyType
{
   public string Name { get; set; } 
}

public class Program
{
    public static void Main()
    {
        var tableName = "customers";
        var cols = new List<MyType>
        {
            new MyType { Name = "surname"},
            new MyType { Name = "firstname"},
            new MyType { Name = "dateOfBirth"}
        };

        var InsertStatement = $"Insert into { tableName } ( {String.Join(", ", cols.Select(col => col.Name))} )";
        Console.WriteLine(InsertStatement);
    }
}
0 голосов
/ 09 февраля 2020

Вы можете легко создать текст следующим образом:

var query = $"INSERT INTO {tableName}({string.Join(",", cols.Select(x=>x.Name))})";

Однако, если для целей обучения , вы собираетесь обрабатывать случай, используя обобщенный метод c, вы можете создайте обобщенную c функцию, подобную следующей, а затем легко используйте for l oop и удалите дополнительный разделитель, используя TrimEnd, или как лучший вариант, например, String.Join реализацию. NET Framework получить перечислитель так:

string Join<TItem>(
    IEnumerable<TItem> items, Func<TItem, string> itemTextSelecor, string separator)
{
    var en = items.GetEnumerator();
    if (!en.MoveNext())
        return String.Empty;
    var builder = new StringBuilder();
    if (en.Current != null)
        builder.Append(itemTextSelecor(en.Current));
    while (en.MoveNext())
    {
        builder.Append(separator);
        if (en.Current != null)
            builder.Append(itemTextSelecor(en.Current));
    }
    return builder.ToString();
}

И использовать его так:

var tableName = "customers";
var cols = new[]
{
    new { Name = "surname"},
    new { Name = "firstname"},
    new { Name = "dateOfBirth"}
};

var InsertStatement = $"INSERT INTO {tableName} ({Join(cols, col => col.Name, ", ")})" 
    +  $"VALUES({Join(cols, col => $"@{col.Name}", ", ")})";
0 голосов
/ 09 февраля 2020

Замените dynamic на object или TValue на ограничение типа, предусматривающее, что это должен быть класс (where TValue : class), и вызовите obj.ToString() вместо просто obj

Однако это не гарантирует, что он будет «работать с любым типом» - для этого вам нужно знать, что все эти типы следуют контракту, чтобы вывести желаемое имя столбца в качестве их строкового представления. Чтобы получить больше конкретики, потребуйте, чтобы принятые вами типы реализовали некоторый интерфейс, например IColumnName, и вместо этого поместите этот интерфейс в ограничение типа

...