Есть ли способ преобразовать строку в тип? - PullRequest
4 голосов
/ 07 мая 2020

Я изучаю дженерики в C#. Так что это может быть просто для опытных людей.

У меня 71 модель, и я хочу иметь возможность хранить в них данные из CSV.

Обработка не очень сложна, я иметь эту сигнатуру метода:

private static async Task ProcessFileAsync<T>(string currentFile) where T : class, new()

Самая сложная часть - это вызов it. У меня есть один CSV-файл для каждой модели, в которую я хочу поместить данные. Имя файла CSV идентично имени модели (например: Product.csv будет соответствовать модели продукта).

В идеале я хотел бы просто отправить имя в вызывающей стороне, но я получаю a "X - переменная, но используется как тип" Ошибка компилятора.

Я мог бы использовать массивный оператор switch для решения этой проблемы, но это кажется относительно расточительным.

Любая помощь будет оценен.

Другими словами, я мог бы сделать следующее:

        switch(justFName)
        {
            case "Address":
                _ = ProcessFileAsync<Address>(ci.FullName);
                break;
            case "Currency":
                _ = ProcessFileAsync<Currency>(ci.FullName);
                break;
                ...
                ...
                ...And so on
                ...
                ...
            default:
                //No method for this file name
                break;
        }

вместо этого я хотел бы иметь что-то вроде этого: _ = ProcessFileAsync<justFName>(ci.FullName);

Ответы [ 2 ]

4 голосов
/ 07 мая 2020

Если вы можете каким-то образом определить все классы, которые вам нужно обрабатывать, из вашей сборки (лично я люблю отмечать их специально созданным атрибутом), тогда Reflection и Expression Trees на помощь :

public class Address { }

public class MethodHolder // Just dummy class to hold your process action
{
    public static async Task ProcessFileAsync<T>(string currentFile) where T : class, new()
    {
        Console.WriteLine(currentFile);
    }
}

public static class Processor
{
    private static readonly Dictionary<string, Action<string>> _dict;
    static Processor()
    {
        var types = typeof(Address).Assembly.GetTypes()
            // filter your types correctly here somehow
            // JIC do not forget to verify that they satisfy
            // your generic constraints
            .Where(t => t.Name == "Address");
        _dict = types.ToDictionary(t => t.Name, BuildAction);
    }

    private static Action<string> BuildAction(Type t)
    {
        var method = typeof(MethodHolder).GetMethod(nameof(MethodHolder.ProcessFileAsync))
        .MakeGenericMethod(t);

        var param = Expression.Parameter(typeof(string));
        return Expression.Lambda<Action<string>>(
                Expression.Call(method, param), 
                param)
            .Compile();
    }

    // TODO: add some nice handling for keys not in dictionary
    public static void Process(string key, string value) => _dict[key](value);
}

И использование: Processor.Process(nameof(Address), "testfilename"); (nameof просто для примера)

0 голосов
/ 07 мая 2020

Ответ @ GuruStron очень хорош. Еще один способ добиться того, что вам нужно, - использовать только отражение. Однако, как предлагает @GuruStrong, для вас полезно включить аннотацию в классы, в которых будет выполняться поиск, или поместить их в одну сборку. Следующий код работает, только если эти классы находятся в одной сборке.

#region These classes must be in the same assembly
public class Address {
}

public class Currency {
} 
#endregion

class Program {
    static async Task Main(string[] args) {
        var justFName = "Currency";
        var fullName = "name";
        var type = typeof(Address).Assembly.GetTypes()
            .Single(x => x.Name == justFName);
        var method = typeof(Program).GetMethod(nameof(ProcessFileAsync),
                BindingFlags.NonPublic | BindingFlags.Static)
            .MakeGenericMethod(type);
        await (Task)method.Invoke(null, new object[] { fullName });

        Console.ReadLine();
    }

    private static async Task ProcessFileAsync<T>(string currentFile) where T : class, new() {
        Console.WriteLine(currentFile);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...