gpt4 book ai didi

c# - 如何处理匿名类型 的强制转换委托(delegate),委托(delegate) T 以便在 IEnumerable 的Where() 方法中使用

转载 作者:行者123 更新时间:2023-12-02 07:13:55 25 4
gpt4 key购买 nike

首先我要说的是,我是菜鸟,我不知道自己在做什么。因此,如果有更好的方法可以做到这一点,我会洗耳恭听。

目前,我正在开发一个项目,我需要能够将数据源强制转换为 List<T> ,其中T是匿名类型,并使用 lambda 表达式对其进行过滤,或者动态创建 lambda 表达式,将它们保存到数据库中。我已经为 System.Linq.Dynamic.Core 创建了一个静态包装类,称为 RunTimeType ,它具有允许我从某些数据源创建匿名类型的方法,然后创建 List<>那种匿名类型。在anontype之后和List<anontype>创建后,我正在使用 existing fluent interface创建 Expression<Func<T, bool>> 。一旦我构建了 Expression并编译它,我要么想执行它,要么想将它转换为字符串并保存到数据库、xml文件等中,以供以后使用。

情况1:

当编译然后立即执行表达式时,直到这一行我都很好:

var testList = anonList.Where(castedExp).ToList();

我收到以下错误:

Error CS1973 C# has no applicable method named 'Where' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.

这是有道理的,因为 filter被声明为 dynamic ,我被迫这样做,否则编译器会提示以下内容:

Error CS1061 'object' does not contain a definition for 'By' and no accessible extension method 'By' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)

情况2:

对于构建表达式的情况,将其转换为字符串,然后编译为有效的 Func<T,TResult> ,直到这一行我都很好:

var castedExp = (Func<dynamic, bool>)compileExp;

我收到以下错误:

Error System.InvalidCastException 'System.Func2[<>f__AnonymousType02[System.String,System.String],System.Boolean]' to type 'System.Func`2[System.Object,System.Boolean]'.'

但是,我知道如果我不明确转换为 Func<dynamic, bool> ,编译器将提示以下内容:

Error CS1503 Argument 2: cannot convert from 'System.Delegate' to 'System.Func<dynamic, bool>'.

所以,我的问题是,如何解决这两种情况,同时仍然保持使用匿名类型的能力。只是为了再次澄清,我被迫创建一个匿名类型,因为我不知道在运行时将获得什么数据集,因为这些数据集是完全动态的。

我想重申,只要满足项目的限制,我愿意以不同的方式做到这一点。坦白说,我已经在这方面工作了一段时间,我没有想法,我需要一些指导。

下面是所有相关代码。

测试代码:

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using ExpressionBuilder.Generics;
using ExpressionBuilder.Common;
using System.Linq;
using System.Linq.Dynamic;
using System.Linq.Dynamic.Core;
using ExpressionBuilterTest.TestImplementations;

namespace ExpressionBuilterTest
{
class Program
{


static void Main(string[] args)
{

//test Data source
object[,] arrayTest = new object[3, 2];

arrayTest[0, 0] = "Field1";
arrayTest[1, 0] = "X1";
arrayTest[2, 0] = "Y1";

arrayTest[0, 1] = "Field2";
arrayTest[1, 1] = "X2";
arrayTest[2, 1] = "Y2";

var anonType = RunTimeType.Create(arrayTest);

var anonList = RunTimeType.CreateGenericList(anonType, arrayTest);

//Creation of List<anonymous> type
var anonList = CreateGenericList(anonType, arrayTest);

//Creation of List<anonymous> type
Type genericFilter = typeof(Filter<>);

Type constructedClass = genericFilter.MakeGenericType(anonType);


//*************************Case 1*************************
/*
use dynamic otherwise compiler complains about accessing
methods on the instance of the filter object
*/
dynamic filter = Activator.CreateInstance(constructedClass);

filter.By("Field1", Operation.Contains, " X1 ")
.Or.By("Field2", Operation.Contains, " X2 ");

//returns Expression<Func<T, bool>>
var lamda = filter.GetExpression();

//Error CS1973
IEnumerable<dynamic> testList = anonList.Where(castedExp).ToList();

Console.WriteLine(testList.Count().ToString());
Console.WriteLine("\n");



//*************************Case 2*************************
//convert to string
string expString = lamda.Body.ToString().Replace("AndAlso", "&&").Replace("OrElse", "||");

// simulation of compiling an expression from a string which would be returned from a database
var param = Expression.Parameter(anonType, ExpressionParameterName.Parent);

var exp = System.Linq.Dynamic.DynamicExpression.ParseLambda(new ParameterExpression[] { param }, typeof(bool), expString);

var compiledExp = exp.Compile();

//*******************************************************
//Error CS1973
'System.Func`2[<>f__AnonymousType0`2[System.String,System.String],System.Boolean]' to type 'System.Func`2[System.Object,System.Boolean]'.'
var castedExp = (Func<dynamic, bool>)compileExp;
//*******************************************************

var testList2 = anonList.Where(castedExp).ToList();

Console.WriteLine(testList2.Count().ToString());
Console.ReadKey();

}

}

}

RunTimeType 类:

(为了简洁起见,我省略了 CreateCreateGenericList 方法的重载)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Runtime.CompilerServices;

namespace ExpressionBuilterTest.TestImplementations
{
public static class RunTimeType
{

/// <summary>
/// Creates an anonymous type from a 2d array that includes headers
/// </summary>
public static Type Create<T>(T[,] fieldNameAndValues)
{
IList<System.Linq.Dynamic.Core.DynamicProperty> properties = new List<System.Linq.Dynamic.Core.DynamicProperty>();

int columnCount = fieldNameAndValues.GetLength(1);

for (int jj = 0; jj < columnCount; jj++)
properties.Add(new System.Linq.Dynamic.Core.DynamicProperty(fieldNameAndValues[0, jj].ToString(), fieldNameAndValues[1, jj].GetType()));

return DynamicClassFactory.CreateType(properties);

}

/// <summary>
/// Creates an IEnumerable<dynamic>, where dynamic is an anonymous type, from a 2d array
/// </summary>
/// <param name="type">Anonymous type</param>
/// <param name="data">2 dimensional array of data</param>
public static IEnumerable<dynamic> CreateGenericList<T>(Type anonType, T[,] data)
{
ThrowIfNotAnonymousType(anonType);

dynamic dynoObject = Activator.CreateInstance(anonType);

var fieldNames = dynoObject.GetDynamicMemberNames();

Type genericListType = typeof(List<>);

Type constructedClass = genericListType.MakeGenericType(anonType);

dynamic list = (IEnumerable<dynamic>)Activator.CreateInstance(constructedClass);

int rowCount = data.GetLength(0);
int jj;

for (int ii = 1; ii < rowCount; ii++) //skip first row
{

jj = 0;

foreach (var field in fieldNames)
anonType.GetProperty(field).SetValue(dynoObject, data[ii, jj], null);
jj++;

list.Add(dynoObject);

}

return list;

}

private static void ThrowIfNotAnonymousType(Type type)
{
if (!IsAnonymousType(type))
throw new Exception("'anonType' must be an anonymous type");

}

//https://stackoverflow.com/questions/1650681/determining-whether-a-type-is-an-anonymous-type
private static Boolean IsAnonymousType(Type type)
{
Boolean hasCompilerGeneratedAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Count() > 0;
Boolean nameContainsAnonymousType = type.FullName.Contains("AnonymousType");
Boolean isAnonymousType = hasCompilerGeneratedAttribute && nameContainsAnonymousType;

return isAnonymousType;
}


}

}

更新:

我合并了 @CSharpie 的答案,并对其进行了定制以适合我的实现。一切都会编译,但是,我没有得到正确的输出(请参阅代码正文中的注释)。

    static void Main(string[] args)
{

object[,] arrayTest = new object[3, 2];

arrayTest[0, 0] = "Field1";
arrayTest[1, 0] = "X1";
arrayTest[2, 0] = "Y1";

arrayTest[0, 1] = "Field2";
arrayTest[1, 1] = "X2";
arrayTest[2, 1] = "Y2";

var anonType = RunTimeType.Create(arrayTest);

var anonList = RunTimeType.CreateGenericList(anonType, arrayTest);


Type targetType = anonType;

Type genericFilter = typeof(Filter<>);

Type constructedClass = genericFilter.MakeGenericType(targetType);

dynamic filter = Activator.CreateInstance(constructedClass);

//Dynamically build expression
filter.By("Field1", Operation.Contains, "X")
.Or.By("Field2", Operation.Contains, "2");

//Returns Expression<Func<anonType, bool>>
var lamda = filter.GetExpression();

string expString = lamda.Body.ToString();
expString = expString.Replace("AndAlso", "&&").Replace("OrElse", "||");

/*
Prints: (((x.Field1 != null) && x.Field1.Trim().ToLower().Contains("X".Trim().ToLower())) || ((x.Field2 != null) &&
x.Field2.Trim().ToLower().Contains("2".Trim().ToLower())))
*/
Console.WriteLine(expString);

ParameterExpression param = Expression.Parameter(targetType, ExpressionParameterName.Parent);

LambdaExpression exp = System.Linq.Dynamic.DynamicExpression.ParseLambda(new ParameterExpression[] { param }, typeof(bool), expString);

Delegate compileExp = exp.Compile();


MethodInfo whereMethod = typeof(Enumerable).GetMethods().Single(m =>
{
if (m.Name != "Where" || !m.IsStatic)
return false;
ParameterInfo[] parameters = m.GetParameters();
return parameters.Length == 2 && parameters[1].ParameterType.GetGenericArguments().Length == 2;
});

MethodInfo finalMethod = whereMethod.MakeGenericMethod(anonType);

IEnumerable resultList = (IEnumerable)finalMethod.Invoke(null, new object[] { anonList, compileExp });

/*
Prints Nothing but should print the following:
X1 X2
*/
foreach (dynamic val in resultList)
{
Console.WriteLine(val.Field1 + "/t" + val.Field2);
}

Console.ReadKey();


}

最终更新:

对于那些感兴趣的人,我终于成功了。我发现我的CreateGenericList方法,仅返回我的匿名类型的第一个实例的列表。这么说CreateGenericList应该变成:

引用:https://github.com/StefH/System.Linq.Dynamic.Core/blob/master/src/System.Linq.Dynamic.Core/DynamicClass.cs

    /// <summary>
/// Creates an IEnumerable<dynamic>, where dynamic is an anonymous type, from a 2d array
/// </summary>
/// <param name="type">Anonymous type</param>
/// <param name="data">2 dimensional array of data</param>
public static IEnumerable<dynamic> CreateGenericList<T>(Type anonType, T[,] data)
{
ThrowIfNotAnonymousType(anonType);

Type genericListType = typeof(List<>);

Type constructedClass = genericListType.MakeGenericType(anonType);

dynamic list = (IEnumerable<dynamic>)Activator.CreateInstance(constructedClass);

//first instance
dynamic dynoObject = Activator.CreateInstance(anonType);

//System.Linq.Dynamic.Core.DynamicClass.GetDynamicMemberNames()
var fieldNames = dynoObject.GetDynamicMemberNames();

int rowCount = data.GetLength(0);
int jj;

for (int ii = 1; ii < rowCount; ii++) //skip first row
{


jj = 0;

foreach (var field in fieldNames)
{

//System.Linq.Dynamic.Core.DynamicClass.SetDynamicPropertyValue()
dynoObject.SetDynamicPropertyValue(field,data[ii, jj]);
jj++;

}
list.Add(dynoObject);

//create a new instance for each iteration of the loop
dynoObject = Activator.CreateInstance(anonType);

}

return list;

}

然后Main变成:

    static void Main(string[] args)
{

object[,] arrayTest = new object[3, 2];

arrayTest[0, 0] = "Field1";
arrayTest[1, 0] = "X1";
arrayTest[2, 0] = "blah";

arrayTest[0, 1] = "Field2";
arrayTest[1, 1] = "Y1";
arrayTest[2, 1] = "Y2";

var anonType = RunTimeType.Create(arrayTest);

var anonList = RunTimeType.CreateGenericList(anonType, arrayTest);

Type genericFilter = typeof(Filter<>);

Type constructedClass = genericFilter.MakeGenericType(anonType);

dynamic filter = Activator.CreateInstance(constructedClass);


//Dynamically build expression
filter.By("Field1", Operation.Contains, "blah")
.Or.By("Field2", Operation.Contains, "2");


//Returns Expression<Func<anonType, bool>>
var lamda = filter.GetExpression();

//Prints: System.Func`2[<>f__AnonymousType0`2[System.String,System.String],System.Boolean]
Console.WriteLine(lamda.Compile().ToString());
Console.WriteLine("\n");

string expBodyString = lamda.Body.ToString();

/*
Prints: (((x.Field1 != null) AndAlso x.Field1.Trim().ToLower().Contains("blah".Trim().ToLower()))
OrElse ((x.Field2 != null) AndAlso x.Field2.Trim().ToLower().Contains("2".Trim().ToLower())))
*/
Console.WriteLine(expBodyString);
Console.WriteLine("\n");

expBodyString = expBodyString.Replace("AndAlso", "&&").Replace("OrElse", "||");

/*
Prints: (((x.Field1 != null) && x.Field1.Trim().ToLower().Contains("blah".Trim().ToLower())) || ((x.Field2 != null)
&& x.Field2.Trim().ToLower().Contains("2".Trim().ToLower())))
*/
Console.WriteLine(expBodyString);
Console.WriteLine("\n");


ParameterExpression param = Expression.Parameter(anonType, ExpressionParameterName.Parent);


LambdaExpression exp = System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(new ParameterExpression[] { param }, typeof(bool), expBodyString);


/*
Prints: (((x.Field1 != null) AndAlso x.Field1.Trim().ToLower().Contains("blah".Trim().ToLower()))
OrElse ((x.Field2 != null) AndAlso x.Field2.Trim().ToLower().Contains("2".Trim().ToLower())))
*/
Console.WriteLine(exp.Body.ToString());
Console.WriteLine("\n");


Delegate compileExp = exp.Compile();

//Prints: System.Func`2[<>f__AnonymousType0`2[System.String,System.String],System.Boolean]
Console.WriteLine(compileExp.ToString());
Console.WriteLine("\n");

MethodInfo whereMethod = typeof(Enumerable).GetMethods().Single(m =>
{
if (m.Name != "Where" || !m.IsStatic)
return false;
ParameterInfo[] parameters = m.GetParameters();
return parameters.Length == 2 && parameters[1].ParameterType.GetGenericArguments().Length == 2;
});

MethodInfo finalMethod = whereMethod.MakeGenericMethod(anonType);

IEnumerable resultList = (IEnumerable)finalMethod.Invoke(null, new object[] { anonList, compileExp });


//Prints: blah Y2
foreach (dynamic val in resultList)
{
Console.WriteLine(val.Field1 + "\t" + val.Field2);
}

Console.ReadKey();

}

最佳答案

这是一个简单的示例,没有任何额外的 nuget 包,调用 Enumerable.Where方法。我不知道您到底使用什么软件包,因此您必须根据您的要求进行调整。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

public class Program
{
static void Main(string[] args)
{
var test = new {Foo = "bar"};
var test2 = new {Foo = "derp"};

// get the annonymous type
Type anonType = test.GetType();


// create a list of that annonymous type
IList genericList = (IList) Activator.CreateInstance(typeof(List<>).MakeGenericType(anonType));

genericList.Add(test);
genericList.Add(test2);




// Find the correct Enumerable.Where method
MethodInfo whereMethod = typeof(Enumerable).GetMethods().Single(m =>
{
if (m.Name != "Where" || !m.IsStatic)
return false;
ParameterInfo[] parameters = m.GetParameters();
return parameters.Length == 2 && parameters[1].ParameterType.GetGenericArguments().Length == 2;
});

// construct the finalmethod using generic type
MethodInfo finalMethod = whereMethod.MakeGenericMethod(anonType);

// define the Type of the Filter Func<anontype,bool>
Type filterType = typeof(Func<,>).MakeGenericType(anonType, typeof(bool));


// Build a simple filter expression
// this is mostly to subsitute for the missing packages you are using to create that filter func
ParameterExpression parameter = Expression.Parameter(anonType, "item");
MemberExpression member = Expression.Property(parameter, "Foo");
BinaryExpression euqalExpression = Expression.Equal(member, Expression.Constant("derp"));

LambdaExpression filterExpression = Expression.Lambda(filterType, euqalExpression, parameter);
Delegate filter = filterExpression.Compile();

Console.WriteLine("This is the Filter: {0}", filterExpression);





// Finally invoke and see it in action
IEnumerable result = (IEnumerable) finalMethod.Invoke(null, new object[] {genericList, filter});

foreach (dynamic o in result)
{
Console.WriteLine(o.Foo);
}

Console.ReadKey();
}
}

这里的问题是,您需要自己构造泛型方法,这意味着您必须提供泛型参数。在你的情况下它是 anonType。这就是这条线正在做的事情

MethodInfo finalMethod = whereMethod.MakeGenericMethod(anonType);

之后,我使用 System.Linq.Expressions 创建一个简单的 Func<anontype, bool>它代表过滤器。

更新

这对我现在有用,缺陷一定是你正在做的其他事情。我把这个问题留给你去解决。

     object[,] arrayTest = new object[3, 2];

arrayTest[0, 0] = "Field1";
arrayTest[1, 0] = "X1";
arrayTest[2, 0] = "derp";

arrayTest[0, 1] = "Field2";
arrayTest[1, 1] = "X2";
arrayTest[2, 1] = "Y2";

var anonType = RunTimeType.Create(arrayTest);

var anonList = ( IList)RunTimeType.CreateGenericList(anonType, arrayTest);




// define the Type of the Filter Func<anontype,bool>
Type filterType = typeof(Func<,>).MakeGenericType(anonType, typeof(bool));


// Build a simple filter expression
ParameterExpression parameter = Expression.Parameter(anonType, "item");


var property = anonType.GetProperty("Field1");
MemberExpression member = Expression.Property(parameter, property);


BinaryExpression euqalExpression = Expression.Equal(member, Expression.Constant("derp"));

MethodInfo whereMethod = typeof(Enumerable).GetMethods().Single(m =>
{
if (m.Name != "Where" || !m.IsStatic)
return false;
ParameterInfo[] parameters = m.GetParameters();
return parameters.Length == 2 && parameters[1].ParameterType.GetGenericArguments().Length == 2;
});
MethodInfo finalMethod = whereMethod.MakeGenericMethod(anonType);
LambdaExpression filterExpression = Expression.Lambda(filterType, euqalExpression, parameter);



Delegate filter = filterExpression.Compile();


Console.WriteLine("This is the Filter: {0}", filterExpression);

IEnumerable result = (IEnumerable) finalMethod.Invoke(null, new object[] {anonList, filter});

foreach (dynamic o in result)
{
Console.WriteLine(o.Field1);
}

Console.ReadKey();

关于c# - 如何处理匿名类型 <T> 的强制转换委托(delegate),委托(delegate) T 以便在 IEnumerable<T> 的Where<T>() 方法中使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59594284/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com