.NET 7 недавно представил IParsable в качестве интерфейса, и я хотел бы проверить его наличие. Некоторый тест, который вернет true, если T реализует IParsable<T>, и false в противном случае.
Скажем, я хочу вернуть объект типа T, когда T имеет метод Parse, например:
T ParseAs<T>(string s)
{
if (typeof(IParsable<T>).IsAssignableFrom(typeof(T)))
{
return T.Parse(s);
}
else
{
//do something else...
}
}
Я надеюсь, что это проверит, реализует ли T IParsable<T> и предоставит мне доступ к методам Parse и TryParse внутри. Кажется, я не могу использовать T в качестве аргумента типа для IParsable, вместо этого получая это исключение:
CS0314 Тип «T» нельзя использовать в качестве параметра типа «TSelf» в универсальном типе или методе «IParsable<TSelf>». Нет преобразования бокса или преобразования параметра типа из «T» в «System.IParsable<T>».
Я также получаю указанную выше ошибку, если пытаюсь использовать is:
s is IParsable<T>
Как бы я решил это?
Это компилируется, но не позволяет мне выполнять T.Parse (ы) - знаете, почему это так?
IParsable
является рекурсивно определенным, поэтому вам нужно установить, что T
есть IParsable<T>
, прежде чем вы сможете сказать is IParsable<T>
...
Правильно ли я вас понимаю, говоря, что прежде чем я смогу проверить, является ли T IParsable<T>, я должен знать, что T является IParsable<T>? Звучит как ловушка-22.
Чтобы иметь возможность использовать синтаксис T.Parse(), вам нужно знать во время компиляции, что T реализует IParseable<T>. Единственный способ убедиться в этом во время компиляции — явно сказать, что:
T ParseAs<T>(string s) where T: IParsable<T> {
return T.Parse(s, null);
}
Если вы просто набрали T без явного указания, что это IParsable<T> - невозможно использовать синтаксис T.Parse, и вы должны использовать отражение полностью. Сначала вы проверяете, реализует ли T этот интерфейс с помощью отражения, а затем снова используете отражение для вызова этого статического метода:
T ParseAs<T>(string s) {
var isParsable = typeof(T).GetInterfaces().Any(c => c.IsGenericType && c.GetGenericTypeDefinition() == typeof(IParsable<>));
if (isParsable) {
var parse = typeof(T).GetMethods(BindingFlags.Static | BindingFlags.Public)
.FirstOrDefault(c => c.Name == "Parse" && c.GetParameters().Length == 2 && c.GetParameters()[0].ParameterType == typeof(string) && c.GetParameters()[1].ParameterType == typeof(IFormatProvider));
if (parse != null)
return (T) parse.Invoke(null, new object[] { s, null });
}
return default(T);
}
Это, конечно, довольно уродливо, и вы, вероятно, не захотите этого делать.
Подход с отражением был тем, который я использовал до этого обновления, и я хотел заменить его чем-то более элегантным. Спасибо за ответ - он точно отвечает на мой вопрос.
Это помогает? stackoverflow.com/a/503359/1974021