.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