【问题标题】:Using runtime-created delegates and objects使用运行时创建的委托和对象
【发布时间】:2017-11-02 00:24:02
【问题描述】:

我一直在为这个一般概念而苦苦挣扎,所以是时候伸出手来了。我有一个 LINQ 查询,它从 SQL 中选择一个字符串列表,然后调用 Select 并提供一个获取字符串并返回对象的静态方法:

MyDataContext context = new MyDataContext();
var list = (
    from t in context.GetTable<FooTable>()
    where t.FooType = userProvidedString
    select t.SomeColumn).ToList()
        .Select(colValue => MyClass.FromString(colVal))
        .ToList();
int unique = list.Distinct(new MyClassComparer()).Count();

我有一些使用相同查询但类型不同的不同“配置文件”。例如,除了 MyClass,我还有 MyOtherClass,它还有一个 FromString 方法,它接受一个字符串参数,但返回一个自身的实例。从查询中取回列表后,我想对其调用 Distinct,提供一个在运行时再次确定的比较器委托。

我在这类事情上经常遇到的问题是构建时的类型。我知道我可以像这样创建比较器:

Type comparerType = Type.GetType(String.Format("MyNamespace.{0}Comparer", userProvidedString));
ConstructorInfo ctor = comparerType.GetConstructor(new Type[] { });

但是当我在 ctor 上调用 Invoke 时,我仍然会得到一个必须强制转换的对象。我确信这是可以做到的,而且我只是缺少一些关键概念。请给我一个顿悟! 提前谢谢...

【问题讨论】:

  • “我确定这可以做到” -- 你确定可以做什么?你的问题很不清楚。你为什么首先使用反射来创建比较器?鉴于您正在使用反射,为什么您认为反射 API 有办法返回对新创建对象的强类型引用?反射 API 应该如何先验地知道您正在创建什么类型的对象?请提供一个很好的minimal reproducible example,清楚地说明您的要求。 准确地解释代码现在做什么以及你想要什么。
  • 你不能把它包装在一个函数中并传入一个转换器/比较器吗?即Func&lt;string&gt; & IConparer&lt;T&gt; 参数
  • 同意上述 2 cmets - 似乎您只想编写 Distinct() 方法来接受 IComparer&lt;string&gt; ,然后在某处使用工厂方法在适当的情况下返回实例?例如甚至不需要反射。
  • 抱歉,不够清晰和精确。应该忽略所有关于反射的漫谈。目标是对多个列表项类型使用相同的查询。在我上面的示例中,这些类型将是 MyClass 和 MyOtherClass。每个都有一个将在查询结果上调用的 FromString 方法。然后将使用与列表项类型对应的比较器生成唯一列表。

标签: c# linq reflection delegates


【解决方案1】:

“建设性的 cmets”让我朝着正确的方向前进,这就是我想出的解决方案。

public void Foo()
{
    string dataType = "MyClass"; // Pretend this string was specified by the user
    Type t = Type.GetType(dataType);
    Type u = Type.GetType(dataType + "Comparer"); // Implements IEqualityComparer<MyClass>
    MethodInfo method = this.GetType().GetMethod("GetUniqueItemCount", BindingFlags.Instance | BindingFlags.NonPublic);
    MethodInfo generic = method.MakeGenericMethod(t, u);

    int count = (int)generic.Invoke(this, new object[] { "some data blah blah" });
}

private void GetUniqueItemCount<T, U>(string data) where T : class
                                                   where U : IEqualityComparer<T>, new()
{
    MyDataContext context = new MyDataContext();
    var list = (
        from v in context.GetTable<SomeTable>()
        select v.SomeColumn).ToList()
            .Select(cellValue => Activator.CreateInstance(typeof(T), new object[] { cellValue }) as T)
            .ToList();
    return list.Distinct(new U()).Count();
}

遗憾的是,我不得不使用 Activator.CreateInstance,因为“new T()”只能与无参数构造函数一起使用。这不是很好,因为查询结果集中的每个项目都会调用它。

我为创建 T 和 U 并调用该方法所做的所有反射都是我在问题帖子中所指的(并希望避免)。此解决方案应充分说明我的意图,因此如果您知道更清洁的方法,请加入。

再次感谢您的帮助!

【讨论】:

    猜你喜欢
    • 2014-03-08
    • 2012-04-19
    • 2021-06-26
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    • 2011-05-27
    • 2018-12-31
    • 2015-02-05
    相关资源
    最近更新 更多