Alasdair Hurst Alasdair Hurst - 1 month ago 12
C# Question

Error with C# lambda expression which includes generic type field comparison - Unity/Mono

I'm trying to implement an extendable class for accessing a database in my Unity game. Models will be used throughout the game, which can be "fetched". This method is part of the base class of

Model<T>
where T is a schema type that matches the database.

Because I have access to T, I can query on the schema using lambda.

Unfortunately, when I try this and the lambda expression is built, I run into a runtime error:

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)


I've ruled out the database connector itself as the issue, and come up with the minimal code to reproduce the issue:

Definitions:

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;
}
}


Instance:

ExpressionObject<IDObject> example = new ExpressionObject<IDObject>();
example.expression();
example.expression(2352);


The first call to the parameterless expression function executes fine and is error free, however the second one throws the error above.

I have also tried wrapping x in a function when accessing the id as such, which results in the same issue.

int idGetter(T ss) {
return ss.id;
}

public void expression(int id) {
Expression<Func<T, bool>> expr = x => idGetter(x) == id;
}


Finally, I made the call a
Func<T, bool>
rather than
Expression<Func<T, bool>>
which resulted in no errors. Unfortunately, I need an
Expression
to pass to the database library that I use, and it also doesn't fix the original issue.

Anyone got any ideas?

Edit: Fixed typo

Edit: This actually works when the expression function has a generic type.

public void expression<S>(int id) where S : IDObject {
Expression<Func<S, bool>> expr = x => x.id == id;
}

example.expression<IDObject>(2352);


This is actually identical to what I had originally, and refactored out. I don't want the caller of something() to care about the
<T>
that the class is using.

Here's an example which can be copied into unity. It's very similar to the example provided. Just drag it onto something and play.

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);
}
}

Answer

This seems like a bug in old versions of Mono. Updating to a beta version of Unity (5.5 b7) fixes the issue.

Comments