通过反射调用本地泛型方法

神秘性

我需要通过反射调用本地通用方法,但我不知道该怎么做。

我的核心问题是我使用反射从一组从 MySQL 数据库读取 EAV 数据的查询中填充 POCO 对象。我很高兴展示这段代码,但它又长又复杂。我创建了一个简化的示例,我认为它很好地总结了问题。

考虑这个代码:

void Main()
{
    var repository = new Repository();
    repository.Store<Foo>(xs => xs.Count());

    int Compute<M>(string[] source) => repository.Fetch<M>().Invoke(source);

    Console.WriteLine(Compute<Foo>(new[] { "A", "B" }));
}

public class Foo { }

public class Repository
{
    private Dictionary<Type, object> _store = new Dictionary<Type, object>();

    public void Store<T>(Func<string[], int> value)
    {
        _store[typeof(T)] = value;
    }

    public Func<string[], int> Fetch<T>()
    {
        return (Func<string[], int>)_store[typeof(T)];
    }
}

我有一个Repository可以存储Func<string[], int>索引类型Type

在我的Main方法中,我实例化了一个实例Repository并存储xs => xs.Count()在类型上Foo

我有一个本地通用方法Compute,当我知道M编译时的类型时,我可以轻松调用它调用Compute<Foo>(new[] { "A", "B" })计算2那没问题。

当我只M在运行时知道时就会出现问题

假设我有这种类型:

public class Bar
{
    public Foo Value;
}

现在我的代码Main如下所示:

void Main()
{
    var repository = new Repository();
    repository.Store<Foo>(xs => xs.Count());

    int Compute<M>(string[] source) => repository.Fetch<M>().Invoke(source);

    var runtimeType = typeof(Bar).GetField("Value").FieldType;

    var reflectedMethod = /* What Goes Here ??? */

    Console.WriteLine(reflectedMethod.Invoke(new[] { "A", "B" }));
}

我完全被这个/* What Goes Here ??? */部分难住了

我发现了一个类似的问题,但该方法不处理本地通用方法。

有人可以帮助如何在运行时调用这样的方法吗?


这是完整的工作代码。它在 LINQPad 中运行,带有 NuGet“System.Interactive”和一个 Maybe monad 库,并引用了我的 MySQL 数据库。

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
internal class MagentoAttribute : Attribute
{
    public string AttributeCode { get; private set; }
    public MagentoAttribute(string code)
    {
        this.AttributeCode = code;
    }
}

public static class Magento
{
    public abstract class ProductBase
    {
        public uint EntityId { get; private set; }
        public string Sku { get; private set; }
        public decimal? Quantity { get; private set; }
    }

    public static T[] GetProducts<T>(this UserQuery @this) where T : ProductBase, new()
    {
        var setEntityId = typeof(ProductBase).GetProperty("EntityId", BindingFlags.Instance | BindingFlags.SetProperty | BindingFlags.NonPublic | BindingFlags.Public);
        var setSku = typeof(ProductBase).GetProperty("Sku", BindingFlags.Instance | BindingFlags.SetProperty | BindingFlags.NonPublic | BindingFlags.Public);
        var setQuantity = typeof(ProductBase).GetProperty("Quantity", BindingFlags.Instance | BindingFlags.SetProperty | BindingFlags.NonPublic | BindingFlags.Public);

        var results =
        (
            from cpe in @this.catalog_product_entity
            where cpe.sku != null && !cpe.sku.StartsWith("SHIP")
            join csi in @this.cataloginventory_stock_item on cpe.entity_id equals csi.product_id
            select new { cpe.entity_id, cpe.sku, csi.qty }
        )
            .ToArray()
            .Select(x =>
            {
                var t = new T();
                setEntityId.SetValue(t, x.entity_id);
                setSku.SetValue(t, x.sku);
                setQuantity.SetValue(t, x.qty);
                return t;
            })
            .ToArray();

        Func<string[], Dictionary<string, Func<uint, Maybe<string>>>> getStrings = attributeCodes =>
            (
                from eet in @this.eav_entity_type
                where eet.entity_type_code == "catalog_product"
                from ea in @this.eav_attribute
                where ea.entity_type_id == eet.entity_type_id
                where attributeCodes.Contains(ea.attribute_code)
                join cpev in @this.catalog_product_entity_varchar on ea.attribute_id equals cpev.attribute_id
                where cpev.store_id == 0
                select new { ea.attribute_code, cpev.entity_id, cpev.value }
            )
            .Concat(
                from eet in @this.eav_entity_type
                where eet.entity_type_code == "catalog_product"
                from ea in @this.eav_attribute
                where ea.entity_type_id == eet.entity_type_id
                where attributeCodes.Contains(ea.attribute_code)
                join cpev in @this.catalog_product_entity_text on ea.attribute_id equals cpev.attribute_id
                where cpev.store_id == 0
                select new { ea.attribute_code, cpev.entity_id, cpev.value }
            )
            .ToArray()
            .GroupBy(x => x.attribute_code, x => new { x.entity_id, x.value })
            .Select(x => new
            {
                attribute_code = x.Key,
                f = x.ToDictionary(y => y.entity_id, y => y.value.ToMaybe()).Map(k => Maybe<string>.Nothing)
            })
            .ToDictionary(x => x.attribute_code, x => x.f);

        Func<string[], Dictionary<string, Func<uint, Maybe<int?>>>> getIntegers = attributeCodes =>
            (
                from eet in @this.eav_entity_type
                where eet.entity_type_code == "catalog_product"
                from ea in @this.eav_attribute
                where ea.entity_type_id == eet.entity_type_id
                where attributeCodes.Contains(ea.attribute_code)
                join cpev in @this.catalog_product_entity_int on ea.attribute_id equals cpev.attribute_id
                where cpev.store_id == 0
                select new { ea.attribute_code, cpev.entity_id, cpev.value }
            )
            .ToArray()
            .GroupBy(x => x.attribute_code, x => new { x.entity_id, x.value })
            .Select(x => new
            {
                attribute_code = x.Key,
                f = x.ToDictionary(y => y.entity_id, y => y.value.ToMaybe()).Map(k => Maybe<int?>.Nothing)
            })
            .ToDictionary(x => x.attribute_code, x => x.f);

        Func<string[], Dictionary<string, Func<uint, Maybe<DateTime?>>>> getDateTimes = attributeCodes =>
            (
                from eet in @this.eav_entity_type
                where eet.entity_type_code == "catalog_product"
                from ea in @this.eav_attribute
                where ea.entity_type_id == eet.entity_type_id
                where attributeCodes.Contains(ea.attribute_code)
                join cpev in @this.catalog_product_entity_datetime on ea.attribute_id equals cpev.attribute_id
                where cpev.store_id == 0
                select new { ea.attribute_code, cpev.entity_id, cpev.value }
            )
            .ToArray()
            .GroupBy(x => x.attribute_code, x => new { x.entity_id, x.value })
            .Select(x => new
            {
                attribute_code = x.Key,
                f = x.ToDictionary(y => y.entity_id, y => y.value.ToMaybe()).Map(k => Maybe<DateTime?>.Nothing)
            })
            .ToDictionary(x => x.attribute_code, x => x.f);

        Func<string[], Dictionary<string, Func<uint, Maybe<decimal?>>>> getDecimals = attributeCodes =>
            (
                from eet in @this.eav_entity_type
                where eet.entity_type_code == "catalog_product"
                from ea in @this.eav_attribute
                where ea.entity_type_id == eet.entity_type_id
                where attributeCodes.Contains(ea.attribute_code)
                join cpev in @this.catalog_product_entity_decimal on ea.attribute_id equals cpev.attribute_id
                where cpev.store_id == 0
                select new { ea.attribute_code, cpev.entity_id, cpev.value }
            )
            .ToArray()
            .GroupBy(x => x.attribute_code, x => new { x.entity_id, x.value })
            .Select(x => new
            {
                attribute_code = x.Key,
                f = x.ToDictionary(y => y.entity_id, y => y.value.ToMaybe()).Map(k => Maybe<decimal?>.Nothing)
            })
            .ToDictionary(x => x.attribute_code, x => x.f);

        var prerepo = new Prerepository();

        prerepo.Store<Maybe<string>>(getStrings);
        prerepo.Store<Maybe<int?>>(getIntegers);
        prerepo.Store<Maybe<DateTime?>>(getDateTimes);
        prerepo.Store<Maybe<decimal?>>(getDecimals);

        var collapse = new Dictionary<Type, Delegate>()
        {
            { typeof(Maybe<int?>), (Func<Maybe<int?>, Maybe<int?>>)(m => m) },
            { typeof(Maybe<int>), (Func<Maybe<int?>, Maybe<int>>)(m => (!m.HasValue || (m.HasValue && m.Value == null)) ? Maybe<int>.Nothing : m.Value.Value.ToMaybe()) },
            { typeof(int?), (Func<Maybe<int?>, int?>)(m => m.HasValue ? m.Value : (int?)null) },
            { typeof(int), (Func<Maybe<int?>, int>)(m => (m.HasValue && m.Value != null) ? m.Value.Value : default(int)) },
            { typeof(Maybe<decimal?>), (Func<Maybe<decimal?>, Maybe<decimal?>>)(m => m) },
            { typeof(Maybe<decimal>), (Func<Maybe<decimal?>, Maybe<decimal>>)(m => (!m.HasValue || (m.HasValue && m.Value == null)) ? Maybe<decimal>.Nothing : m.Value.Value.ToMaybe()) },
            { typeof(decimal?), (Func<Maybe<decimal?>, decimal?>)(m => m.HasValue ? m.Value : (decimal?)null) },
            { typeof(decimal), (Func<Maybe<decimal?>, decimal>)(m => (m.HasValue && m.Value != null) ? m.Value.Value : default(decimal)) },
            { typeof(Maybe<DateTime?>), (Func<Maybe<DateTime?>, Maybe<DateTime?>>)(m => m) },
            { typeof(Maybe<DateTime>), (Func<Maybe<DateTime?>, Maybe<DateTime>>)(m => (!m.HasValue || (m.HasValue && m.Value == null)) ? Maybe<DateTime>.Nothing : m.Value.Value.ToMaybe()) },
            { typeof(DateTime?), (Func<Maybe<DateTime?>, DateTime?>)(m => m.HasValue ? m.Value : (DateTime?)null) },
            { typeof(Maybe<string>), (Func<Maybe<string>, Maybe<string>>)(m => m) },
            { typeof(string), (Func<Maybe<string>, string>)(m => m.HasValue ? m.Value : default(string)) },
        };

        var attributes =
            Enumerable
                .Concat(
                    typeof(T)
                        .GetFields(BindingFlags.GetField | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                        .Select(x => new
                        {
                            Field = x,
                            Attribute = x.GetCustomAttribute<MagentoAttribute>(),
                        })
                        .Where(x => x.Attribute != null)
                        .Select(x => new
                        {
                            Inject = (Action<object, object>)((o, v) => x.Field.SetValue(o, v)),
                            x.Attribute.AttributeCode,
                            AttributeType = x.Field.FieldType
                        }),
                    typeof(T)
                        .GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                        .Select(x => new
                        {
                            Property = x,
                            Attribute = x.GetCustomAttribute<MagentoAttribute>(),
                        })
                        .Where(x => x.Attribute != null)
                        .Select(x => new
                        {
                            Inject = (Action<object, object>)((o, v) => x.Property.SetValue(o, v)),
                            x.Attribute.AttributeCode,
                            AttributeType = x.Property.PropertyType
                        }))
                .Where(x => collapse.ContainsKey(x.AttributeType))
                .ToArray();

        var postrepo = new Postrepository();

        postrepo.Store<Maybe<int?>>(null);

        void Fetch<M>(string[] attributeCodes) => postrepo.Store<M>(prerepo.Fetch<M>()(attributeCodes));

        void InvokeHelper(Action<string[]> prototype, Type type, object data)
        {
            var method = prototype.Method;
            var genericMethod = method.GetGenericMethodDefinition();
            var concreteMethod = genericMethod.MakeGenericMethod(type);
            concreteMethod.Invoke(prototype.Target, new[] { data });
        }

        Type lift(Type t)
        {
            if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Maybe<>))
            {
                t = t.GetGenericArguments()[0];
            }
            if (!t.IsGenericType)
            {
                if (t.IsValueType)
                {
                    t = typeof(Nullable<>).MakeGenericType(t);
                }
            }
            t = typeof(Maybe<>).MakeGenericType(t);
            return t;
        }

        (
            from a in attributes
            let m = lift(a.AttributeType)
            group a.AttributeCode by m
        )
            .ForEach(x => InvokeHelper(Fetch<object>, x.Key, x.Distinct().ToArray()));

        void Inject<M, P>(string attributeCode, Action<object, object> inject)
        {
            var b = postrepo.Fetch<M>();
            var d = b[attributeCode];
            var fmp = (Func<M, P>)collapse[typeof(P)];
            results
                .ForEach(r =>
                {
                    var m = d(r.EntityId);
                    inject(r, fmp(m));
                });
        }

        void InvokeHelper2(Action<string, Action<object, object>> prototype, Type[] types, object[] data)
        {
            var method = prototype.Method;
            var genericMethod = method.GetGenericMethodDefinition();
            var concreteMethod = genericMethod.MakeGenericMethod(types);
            concreteMethod.Invoke(prototype.Target, data);
        }

        (
            from a in attributes
            let m = lift(a.AttributeType)
            group new { a.AttributeType, a.Inject } by new { a.AttributeCode, m }
        )
            .ForEach(xs =>
            {
                xs
                    .ForEach(y =>
                    {
                        InvokeHelper2(Inject<object, object>, new[] { xs.Key.m, y.AttributeType }, new object[] { xs.Key.AttributeCode, y.Inject });
                    });
            });

        return results;
    }

    public class Prerepository
    {
        private Dictionary<Type, object> _store = new Dictionary<Type, object>();

        public void Store<T>(Func<string[], Dictionary<string, Func<uint, T>>> value)
        {
            _store[typeof(T)] = value;
        }

        public Func<string[], Dictionary<string, Func<uint, T>>> Fetch<T>()
        {
            return (Func<string[], Dictionary<string, Func<uint, T>>>)_store[typeof(T)];
        }
    }

    public class Postrepository
    {
        private Dictionary<Type, object> _store = new Dictionary<Type, object>();

        public void Store<T>(Dictionary<string, Func<uint, T>> value)
        {
            _store[typeof(T)] = value;
        }

        public Dictionary<string, Func<uint, T>> Fetch<T>()
        {
            return (Dictionary<string, Func<uint, T>>)_store[typeof(T)];
        }
    }
}

现在我可以编写这段代码:

public class Product : Magento.ProductBase
{
    [Magento("name")] public string Name { get; private set; }

    [Magento("is_deleted")] private Maybe<int?> _is_deleted = Maybe<int?>.Nothing;
    public bool IsDeleted { get => _is_deleted.HasValue ? _is_deleted.Value == 1 : false; }

    [Magento("brand")] private Maybe<string> _brand { get; set; } = Maybe<string>.Nothing;
    public string Brand { get => _brand.HasValue ? _brand.Value : "(missing)"; }

    [Magento("cost")] public decimal Cost { get; private set; }
}

然后这个:

var ps =
    Magento
        .GetProducts<Product>(this)
        .Where(x => x.Cost == 0m)
        .Where(x => !x.IsDeleted)
        .Where(x => x.Quantity > 0m);

我现在有一种强类型的方式来读取 Magento EAV 数据结构。

塞巴斯蒂安·舒曼

Jon Skeets 的回答也适用于您的问题。

我只更改了签名以符合您的要求:

var repository = new Repository();
repository.Store<Foo>(xs => xs.Count());

int Compute<M>(string[] source) => repository.Fetch<M>().Invoke(source);

// Call Compute<M>
var runtimeKnownTime = typeof(Foo);
var computeResult = InvokeHelper(Compute<object>, new[] { "A", "B" }, runtimeKnownTime);

Console.WriteLine(computeResult);

这使用以下实现InvokeHelper

// This code was created by Jon Skeet : https://stackoverflow.com/a/43349127/2729609
// I only changed Action<int> to Func<int, int> and changed the return type.
private static int InvokeHelper(Func<string[], int> int32Func, object data, Type type)
{
    // You probably want to validate that it really is a generic method...
    var method = int32Func.Method;
    var genericMethod = method.GetGenericMethodDefinition();
    var concreteMethod = genericMethod.MakeGenericMethod(type);
    return (int)concreteMethod.Invoke(int32Func.Target, new[] { data });
}

演示

我认为没有必要使用这个 hack。在您的情况下,您不需要泛型类型。将存储库更改为:

public class Repository
{
    private Dictionary<Type, object> _store = new Dictionary<Type, object>();

    public void Store(Type id, Func<string[], int> value)
    {
        _store[id] = value;
    }

    // optional
    public void Store<T>(Func<string[], int> value) => this.Store(typeof(T), value);

    public Func<string[], int> Fetch(Type id)
    {
        return (Func<string[], int>)_store[id];
    }

    // optional
    public Func<string[], int> Fetch<T>() => this.Fetch(typeof(T));
}

您可以在没有泛型的情况下使用它:

var repository = new Repository();
repository.Store(typeof(Foo), xs => xs.Count());

int Compute(Type id, string[] source) => repository.Fetch(id).Invoke(source);

// Call Compute<M>
var runtimeKnownTime = typeof(Foo);

Console.WriteLine(Compute(runtimeKnownTime, new[] { "A", "B" }));

如果需要,可以为这些方法创建通用重载,FetchStore使用typeof(T). optional在我的示例实现中标记了这种方法

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

通过反射通过表达式调用泛型方法

来自分类Dev

通过反射访问/执行泛型方法?

来自分类Dev

通过带有委托参数的反射调用泛型方法的问题

来自分类Dev

通过反射调用具有params参数的泛型方法

来自分类Dev

将lambda作为参数分配给通过反射调用的泛型方法

来自分类Dev

通过派生类型使用反射调用泛型基类上的方法

来自分类Dev

通过传递参数调用泛型方法?

来自分类Dev

通过反射实例化泛型类

来自分类Dev

通过反射调用带有参数的泛型方法时,无法将类型为System.Int32的对象转换为System.Object []

来自分类Dev

通过反射将子类类型传递给Java泛型方法不会引发异常

来自分类Dev

如何调用泛型方法?

来自分类Dev

在泛型上调用方法?

来自分类Dev

泛型的调用对象方法

来自分类Dev

如何从泛型方法调用非泛型方法

来自分类Dev

Java泛型-从泛型类型的方法调用特定方法

来自分类Dev

从非泛型方法调用泛型方法

来自分类Dev

访问通过反射调用的方法返回的IEnumerable <T>的T型属性

来自分类Dev

泛型-调用带有泛型参数的方法

来自分类Dev

在Java中使用泛型和反射实现接口方法

来自分类Dev

反射性泛型类型范围和方法链接

来自分类Dev

在泛型方法中调用重载方法

来自分类Dev

无法通过Java反射调用方法:NoSuchMethodException

来自分类Dev

mockito:通过反射模拟参数的方法调用

来自分类Dev

通过反射调用方法时的歧义匹配

来自分类Dev

通过反射检索的变量类型的方法调用

来自分类Dev

通过反射调用嵌套的DeepObject方法

来自分类Dev

虚拟泛型方法调用的静态分析

来自分类Dev

从Scala调用Java通配符泛型方法

来自分类Dev

如何从泛型类型调用重载方法

Related 相关文章

热门标签

归档