Я определил интерфейс и класс следующим образом:
public interface IXmlKey
{
string Name { get; set; }
string Value { get; set; }
bool HasChildren { get; set; }
}
public interface IXmlKey<T> : IXmlKey where T : IXmlKey<T>
{
public T[] Children { get; set; }
}
и метод:
private static bool ReadSubXmlKeys(XmlReader subReader, IXmlKey objInstance, bool hasChildren, out string error)
{
error = null;
var childrenProp = objInstance.GetType().GetProperties().Single(p => p.Name.Equals("Children"));
var childType = childrenProp.PropertyType.GetElementType();
var childPis = childType.GetProperties();
var generateList = typeof(List<>).MakeGenericType(childType);
var childrenList = Activator.CreateInstance(generateList);
var childrenListType = childrenList.GetType();
//public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
var fun = typeof(Func<,>).MakeGenericType(childType, typeof(bool));
var anyMethod = typeof(Enumerable).GetMethodWithLinq("Any", typeof(IEnumerable<>), typeof(Func<,>)).MakeGenericMethod(childType);
var startNodeName = objInstance.Name;
do
{
var nodeName = subReader.Name;
//var isExist = anyMethod.Invoke(null, [childrenList, <what here>]); ---> here
}
while (subReader.Read());
return true;
}
Как видите, я не уверен, какой параметр следует вызывать.
List<T>.Any(t => t.Name.Equals(nodeName )) where T : IXmlKey
не демо-код. в цикле do { ... } while() я проверю, существует ли имя узла уже или нет. если существует, выдать исключение. в противном случае добавьте в список
Мне непонятно, зачем вам рефлексия. Почему вы не можете запросить XML-данные напрямую? Пожалуйста, объясните, чего вы пытаетесь достичь.
@OlivierJacot-Decombes, моя проблема в том, что я не знаком с параметром отражения вызова общего статического метода, который представляет собой лямбда-выражение. Что касается чтения XML, то это просто бизнес-сцена.
Вам нужно передать предикат Func<childType,bool>
.
Один из способов получить его экземпляр — динамически создать выражение и скомпилировать его следующим образом:
...
var captureClass = new Capture();
var parameterExpression = Expression.Parameter(childType, "t");
var propertyInfo = childType.GetProperty(nameof(IXmlKey.Name));
var propertyCall = Expression.Property(parameterExpression, propertyInfo);
var fieldAccess = Expression.Field(Expression.Constant(captureClass, typeof(Capture)), nameof(Capture.Name));
var equalsMethod = typeof(string).GetMethod("Equals", [typeof(string)]);
var expCall = Expression.Call(propertyCall, equalsMethod, fieldAccess);
var lambda = Expression.Lambda(expCall, [parameterExpression]);
var compiledPredicate = lambda.Compile();
...
do
{
captureClass.Name = subReader.Name;
var isExist = anyMethod.Invoke(null, [childrenList, compiledPredicate]);
}
while (subReader.Read());
return true;
для оптимизации вам также необходимо иметь этот класс:
public class Capture {
public string Name;
}
полный пример на dotnetfiddle
Однако, на мой взгляд, есть лучшая альтернатива: просто создать общий метод для пересылки с вашей исходной Any
логикой:
private static bool ReadSubXmlKeys<T>(XmlReader subReader, T objInstance, bool hasChildren, out string error)
where T:IXmlKey<T>{
var name = "example";
var result = objInstance.Children.Any(t => t.Name.Equals(name));
}
Вам все еще нужно немного подумать, чтобы вызвать его в начале исходного необщего метода:
var genericDefinition = objInstance.GetType().GetInterfaces()
.Where(x => x.IsGenericType)
.Where(x => x.GetGenericTypeDefinition() == typeof(IXmlKey<>));
if (genericDefinition.Count() == 1) {
// you can cache this in a static field too
var genericMethodInfo = typeof(YourTypeThatHasTheStaticMethods)
.GetMethod(nameof(ReadSubXmlKeysGeneric), BindingFlags.NonPublic | BindingFlags.Static);
var genericMethod = genericMethodInfo.MakeGenericMethod(
genericDefinition.First().GetGenericArguments()[0]);
return (bool)genericMethod.Invoke(null, [subReader, objInstance...]);
}
... no need for the first solution
... since it relied on the Children property which is only
... there for IXmlKey<T>
ну, большое спасибо. это полезно
Поскольку делегаты контравариантны по типам параметров, вы можете просто передать Func<IXmlKey, bool>
, который можно написать напрямую. Это совместимо с Func<childType, bool>
.
@Подметальная машина. я делал тесты. один: частный статический bool IsSame(IXmlKey objInstance) { ... }, второй: Func<IXmlKey, bool> f = item => { .... } . оба они не огонь.
@goldii Является ли childType
типом значения? Это действительно не сработает. Что вы имеете в виду под «не огонь»? Это вызвало исключение?
@Sweeper, 1. childType — это ссылочный тип. 2. отладить и запустить AnyMethod.Invoke(null, [childrenList, <что здесь>]); , вы увидите IsSame(IXmlKey objInstance) { ... }, Func<IXmlKey, bool> f = item => { .... } не войдет в
Activator.CreateInstance(generateList)
образует пустой список. Почему вы пытаетесь вызватьAny
из пустого списка? Или это просто демонстрационный код, иллюстрирующий проблему, а в вашем реальном коде что-то другое?