包含泛型字段比较的 C# lambda 表达式出错 - Unity/Mono
Error with C# lambda expression which includes generic type field comparison - Unity/Mono
我正在尝试实现一个可扩展的 class 来访问我的 Unity 游戏中的数据库。模型将在整个游戏中使用,可以是"fetched"。此方法是 Model<T>
基础 class 的一部分,其中 T 是与数据库匹配的架构类型。
因为我可以访问 T,所以我可以使用 lambda 查询架构。
不幸的是,当我尝试此操作并构建 lambda 表达式时,我 运行 陷入 运行 时间错误:
ArgumentException: The field handle and the type handle are incompatible.
System.Reflection.FieldInfo.GetFieldFromHandle (RuntimeFieldHandle handle, RuntimeTypeHandle declaringType) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/FieldInfo.cs:171)
ExpressionObject`1[IDObject].expression (Int32 id) (at Assets/Scripts/ELB/Test/ExistingDBScript.cs:24)
ExistingDBScript.Start () (at Assets/Scripts/ELB/Test/ExistingDBScript.cs:35)
我已经排除了数据库连接器本身的问题,并提出了最少的代码来重现该问题:
定义:
public class IDObject {
public int id;
}
public class ExpressionObject<T> where T : IDObject {
public void expression() {
Expression<Func<T, bool>> expr = x => x.id == 0;
}
public void expression(int id) {
Expression<Func<T, bool>> expr = x => x.id == id;
}
}
实例:
ExpressionObject<IDObject> example = new ExpressionObject<IDObject>();
example.expression();
example.expression(2352);
对无参数表达式函数的第一次调用执行良好并且没有错误,但是第二次调用抛出上述错误。
我也试过在访问 id 时将 x 包装在一个函数中,这导致了同样的问题。
int idGetter(T ss) {
return ss.id;
}
public void expression(int id) {
Expression<Func<T, bool>> expr = x => idGetter(x) == id;
}
最后,我调用了 Func<T, bool>
而不是 Expression<Func<T, bool>>
,这没有导致任何错误。不幸的是,我需要一个 Expression
来传递到我使用的数据库库,它也没有解决原来的问题。
有人有什么想法吗?
编辑:修正错别字
编辑:当表达式函数具有泛型类型时,这实际上有效。
public void expression<S>(int id) where S : IDObject {
Expression<Func<S, bool>> expr = x => x.id == id;
}
example.expression<IDObject>(2352);
这实际上和我原来的一模一样,被重构了。我不希望 something() 的调用者关心 class 正在使用的 <T>
。
这是一个可以复制到unity中的例子。它与提供的示例非常相似。只需将它拖到某物上即可播放。
using UnityEngine;
using System.Linq.Expressions;
using System;
public class IDObject {
public int id;
}
public class ExpressionObject<T> where T : IDObject {
public void expression() {
Expression<Func<T, bool>> expr = x => x.id == 0;
}
public void expression<S>(int id) where S : IDObject {
Expression<Func<S, bool>> expr = x => x.id == id;
}
public void expression(int id) {
Expression<Func<T, bool>> expr = x => x.id == id;
}
}
public class ExtendedExpression : ExpressionObject<IDObject> {
// This is the workaround
public void expressionExt(int id) {
expression<IDObject>(id);
}
}
public class ExistingDBScript : MonoBehaviour {
// Use this for initialization
void Start () {
ExtendedExpression example = new ExtendedExpression();
// This works
example.expression();
// This also works
example.expressionExt(2352);
ExpressionObject<IDObject> example2 = new ExpressionObject<IDObject>();
// This works
example2.expression();
// This does not work
example2.expression(2352);
}
}
在你的第二个代码块中:
public void expression(int id) {
Expression<Func<T, bool>> expr = x => x.id == id;
}
总是会失败,因为 T
和 int
之间不可能进行相等比较,现在可能有 syntactic sugar 允许您编写它,即使它会在运行时失败.
要删除对 int
的依赖,您可以使用通用谓词:
public static class Extensions
{
// checks if a property equals something, generic way
public static bool IsEqualTo<T, TValue>(this T source, Expression<Func<T, TValue>> expression,
Predicate<TValue> predicate)
{
if (expression == null) throw new ArgumentNullException("expression");
if (predicate == null) throw new ArgumentNullException("predicate");
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
throw new ArgumentOutOfRangeException("expression");
var propertyInfo = memberExpression.Member as PropertyInfo;
if (propertyInfo == null)
throw new ArgumentOutOfRangeException("expression");
var value = propertyInfo.GetValue(source);
var value1 = (TValue) value;
var b = predicate(value1);
return b;
}
}
internal class Demo
{
private static void Test()
{
var data = new MyData();
var b1 = data.IsEqualTo(s => s.Valid, t => t == false);
var b2 = data.IsEqualTo(s => s.Number, t => t == 1234);
}
private struct MyData
{
public bool Valid { get; set; }
public int Number { get; set; }
}
}
这只是一个用于读取和比较的示例,可以扩充以写入值等...
这似乎是旧版本 Mono 中的错误。更新到 Unity 测试版 (5.5 b7) 可以解决此问题。
我正在尝试实现一个可扩展的 class 来访问我的 Unity 游戏中的数据库。模型将在整个游戏中使用,可以是"fetched"。此方法是 Model<T>
基础 class 的一部分,其中 T 是与数据库匹配的架构类型。
因为我可以访问 T,所以我可以使用 lambda 查询架构。
不幸的是,当我尝试此操作并构建 lambda 表达式时,我 运行 陷入 运行 时间错误:
ArgumentException: The field handle and the type handle are incompatible.
System.Reflection.FieldInfo.GetFieldFromHandle (RuntimeFieldHandle handle, RuntimeTypeHandle declaringType) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/FieldInfo.cs:171)
ExpressionObject`1[IDObject].expression (Int32 id) (at Assets/Scripts/ELB/Test/ExistingDBScript.cs:24)
ExistingDBScript.Start () (at Assets/Scripts/ELB/Test/ExistingDBScript.cs:35)
我已经排除了数据库连接器本身的问题,并提出了最少的代码来重现该问题:
定义:
public class IDObject {
public int id;
}
public class ExpressionObject<T> where T : IDObject {
public void expression() {
Expression<Func<T, bool>> expr = x => x.id == 0;
}
public void expression(int id) {
Expression<Func<T, bool>> expr = x => x.id == id;
}
}
实例:
ExpressionObject<IDObject> example = new ExpressionObject<IDObject>();
example.expression();
example.expression(2352);
对无参数表达式函数的第一次调用执行良好并且没有错误,但是第二次调用抛出上述错误。
我也试过在访问 id 时将 x 包装在一个函数中,这导致了同样的问题。
int idGetter(T ss) {
return ss.id;
}
public void expression(int id) {
Expression<Func<T, bool>> expr = x => idGetter(x) == id;
}
最后,我调用了 Func<T, bool>
而不是 Expression<Func<T, bool>>
,这没有导致任何错误。不幸的是,我需要一个 Expression
来传递到我使用的数据库库,它也没有解决原来的问题。
有人有什么想法吗?
编辑:修正错别字
编辑:当表达式函数具有泛型类型时,这实际上有效。
public void expression<S>(int id) where S : IDObject {
Expression<Func<S, bool>> expr = x => x.id == id;
}
example.expression<IDObject>(2352);
这实际上和我原来的一模一样,被重构了。我不希望 something() 的调用者关心 class 正在使用的 <T>
。
这是一个可以复制到unity中的例子。它与提供的示例非常相似。只需将它拖到某物上即可播放。
using UnityEngine;
using System.Linq.Expressions;
using System;
public class IDObject {
public int id;
}
public class ExpressionObject<T> where T : IDObject {
public void expression() {
Expression<Func<T, bool>> expr = x => x.id == 0;
}
public void expression<S>(int id) where S : IDObject {
Expression<Func<S, bool>> expr = x => x.id == id;
}
public void expression(int id) {
Expression<Func<T, bool>> expr = x => x.id == id;
}
}
public class ExtendedExpression : ExpressionObject<IDObject> {
// This is the workaround
public void expressionExt(int id) {
expression<IDObject>(id);
}
}
public class ExistingDBScript : MonoBehaviour {
// Use this for initialization
void Start () {
ExtendedExpression example = new ExtendedExpression();
// This works
example.expression();
// This also works
example.expressionExt(2352);
ExpressionObject<IDObject> example2 = new ExpressionObject<IDObject>();
// This works
example2.expression();
// This does not work
example2.expression(2352);
}
}
在你的第二个代码块中:
public void expression(int id) {
Expression<Func<T, bool>> expr = x => x.id == id;
}
总是会失败,因为 T
和 int
之间不可能进行相等比较,现在可能有 syntactic sugar 允许您编写它,即使它会在运行时失败.
要删除对 int
的依赖,您可以使用通用谓词:
public static class Extensions
{
// checks if a property equals something, generic way
public static bool IsEqualTo<T, TValue>(this T source, Expression<Func<T, TValue>> expression,
Predicate<TValue> predicate)
{
if (expression == null) throw new ArgumentNullException("expression");
if (predicate == null) throw new ArgumentNullException("predicate");
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
throw new ArgumentOutOfRangeException("expression");
var propertyInfo = memberExpression.Member as PropertyInfo;
if (propertyInfo == null)
throw new ArgumentOutOfRangeException("expression");
var value = propertyInfo.GetValue(source);
var value1 = (TValue) value;
var b = predicate(value1);
return b;
}
}
internal class Demo
{
private static void Test()
{
var data = new MyData();
var b1 = data.IsEqualTo(s => s.Valid, t => t == false);
var b2 = data.IsEqualTo(s => s.Number, t => t == 1234);
}
private struct MyData
{
public bool Valid { get; set; }
public int Number { get; set; }
}
}
这只是一个用于读取和比较的示例,可以扩充以写入值等...
这似乎是旧版本 Mono 中的错误。更新到 Unity 测试版 (5.5 b7) 可以解决此问题。