admin管理员组

文章数量:1317898

I'm trying to get an out object value from an expression with delegate, but it return null. My code is

internal class TableInfoExtractor
{

    private TableInfoExtractor()
    {

    }

    public static TableInfoExtractor Extractor { get; } = new TableInfoExtractor();

    public bool TryGetTableIndex(ITableInfoCache tableInfoCache, Type userDataType, out object tableInfo, out string error)
    {
        tableInfo = null;
        error = null;


        var t = tableInfoCache.GetType();
        var mm = t.GenericTypeArguments[0];

        var tableInfoCacheType = typeof(TableInfoCache<>).MakeGenericType(mm);

        try
        {

            //internal bool TryGetTableInfo<TUserData>(out TableInfo<TDataBaseProvider, TUserData> tableInfo, out string error) 

            var method = t.GetMethod("TryGetTableInfo", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(userDataType);

            var p1 = Expression.Parameter(typeof(ITableInfoCache)); //TryGetTableInfoCall->ITableInfoCache tableInfoCache

            var tableInfoCacheExp = Expression.Convert(p1, tableInfoCacheType);


            var objType = typeof(object).MakeByRefType();//TryGetTableInfoCall->out object tableInfo
            var p2 = Expression.Parameter(objType, "tableInfo");


            var tableInfoType = typeof(TableInfo<,>).MakeGenericType(mm, userDataType);
            var tableInfoExp = Expression.Convert(p2, tableInfoType);

            var errorExp = Expression.Parameter(typeof(string).MakeByRefType());//TryGetTableInfoCall->out string error

            var callExp = Expression.Call(tableInfoCacheExp, method, tableInfoExp, errorExp);

            var fun = Expression.Lambda<TryGetTableInfoCall>(callExp, p1, p2, errorExp).Compile();
            
            var ret = fun.Invoke(tableInfoCache, out tableInfo, out error); <-- here , ret=>true; tableInfo => null (this is wrong, it should have a value)

            return ret;
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }

        throw new NotImplementedException();
    }
}

as the code point, fun.Invoke(tableInfoCache, out tableInfo, out error); invoked success . If pressed F11, VS can step into method TryGetTableInfo. and the method runs ok, the out parameter tableInfo has value. But when step out the method, tableInfo will be null. i don't know why, and how do i fix it.

the other relation code

internal class TableInfoCache<TDataBaseProvider> : ITableInfoCache where TDataBaseProvider : class
{
    internal bool TryGetTableInfo<TUserData>(out TableInfo<TDataBaseProvider, TUserData> tableInfo, out string error) where TUserData : class
    {
        error = null;
        tableInfo = 
        
        return true;
    }
}


internal class TableInfo<TDataBaseProvider, TUserData> where TDataBaseProvider : class where TUserData : class
{
    
}

internal interface ITableInfoCache
{
}

internal class UserInfo
{
    
    
}

internal class DataBaseProvider
{
    
}

internal delegate bool TryGetTableInfoCall(ITableInfoCache tableInfoCache, out object tableInfo, out string error);

Call the method

var tableInfoCache = new TableInfoCache<DataBaseProvider>();

TableInfoExtractor.Extractor.TryGetTableIndex(tableInfoCache, typeof(UserInfo), out var tableInfo, out error);

online demo

I'm trying to get an out object value from an expression with delegate, but it return null. My code is

internal class TableInfoExtractor
{

    private TableInfoExtractor()
    {

    }

    public static TableInfoExtractor Extractor { get; } = new TableInfoExtractor();

    public bool TryGetTableIndex(ITableInfoCache tableInfoCache, Type userDataType, out object tableInfo, out string error)
    {
        tableInfo = null;
        error = null;


        var t = tableInfoCache.GetType();
        var mm = t.GenericTypeArguments[0];

        var tableInfoCacheType = typeof(TableInfoCache<>).MakeGenericType(mm);

        try
        {

            //internal bool TryGetTableInfo<TUserData>(out TableInfo<TDataBaseProvider, TUserData> tableInfo, out string error) 

            var method = t.GetMethod("TryGetTableInfo", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(userDataType);

            var p1 = Expression.Parameter(typeof(ITableInfoCache)); //TryGetTableInfoCall->ITableInfoCache tableInfoCache

            var tableInfoCacheExp = Expression.Convert(p1, tableInfoCacheType);


            var objType = typeof(object).MakeByRefType();//TryGetTableInfoCall->out object tableInfo
            var p2 = Expression.Parameter(objType, "tableInfo");


            var tableInfoType = typeof(TableInfo<,>).MakeGenericType(mm, userDataType);
            var tableInfoExp = Expression.Convert(p2, tableInfoType);

            var errorExp = Expression.Parameter(typeof(string).MakeByRefType());//TryGetTableInfoCall->out string error

            var callExp = Expression.Call(tableInfoCacheExp, method, tableInfoExp, errorExp);

            var fun = Expression.Lambda<TryGetTableInfoCall>(callExp, p1, p2, errorExp).Compile();
            
            var ret = fun.Invoke(tableInfoCache, out tableInfo, out error); <-- here , ret=>true; tableInfo => null (this is wrong, it should have a value)

            return ret;
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }

        throw new NotImplementedException();
    }
}

as the code point, fun.Invoke(tableInfoCache, out tableInfo, out error); invoked success . If pressed F11, VS can step into method TryGetTableInfo. and the method runs ok, the out parameter tableInfo has value. But when step out the method, tableInfo will be null. i don't know why, and how do i fix it.

the other relation code

internal class TableInfoCache<TDataBaseProvider> : ITableInfoCache where TDataBaseProvider : class
{
    internal bool TryGetTableInfo<TUserData>(out TableInfo<TDataBaseProvider, TUserData> tableInfo, out string error) where TUserData : class
    {
        error = null;
        tableInfo = 
        
        return true;
    }
}


internal class TableInfo<TDataBaseProvider, TUserData> where TDataBaseProvider : class where TUserData : class
{
    
}

internal interface ITableInfoCache
{
}

internal class UserInfo
{
    
    
}

internal class DataBaseProvider
{
    
}

internal delegate bool TryGetTableInfoCall(ITableInfoCache tableInfoCache, out object tableInfo, out string error);

Call the method

var tableInfoCache = new TableInfoCache<DataBaseProvider>();

TableInfoExtractor.Extractor.TryGetTableIndex(tableInfoCache, typeof(UserInfo), out var tableInfo, out error);

online demo https://dotnetfiddle/AWpQfM

Share Improve this question edited Jan 22 at 15:50 DarkBee 15.6k8 gold badges72 silver badges117 bronze badges asked Jan 22 at 15:45 longlong 861 silver badge9 bronze badges 4
  • Slightly altered the fiddle: dotnetfiddle/EWJaAl => What the .... ? That's really odd. – Fildor Commented Jan 22 at 16:08
  • I am clearly missing something obvious. Setting "error" works. – Fildor Commented Jan 22 at 16:13
  • Problem seems to be already here var ret = fun.Invoke(tableInfoCache, out tableInfo, out error); – Fildor Commented Jan 22 at 16:27
  • I cannot prove it rn, but my main suspect as of now is var tableInfoExp = Expression.Convert(p2, tableInfoType); failing to do what it is expected to do. – Fildor Commented Jan 22 at 16:33
Add a comment  | 

1 Answer 1

Reset to default 2

I haven't looked at the disassembly but I think the result of the Convert expression is declaring silently a local variable that is passed to TryGetTableInfo. It is assigned to there, but the parameter it was converted from is left unchanged - the one we actually get back.

So, a solution would be to make this variable assignment explicit and after the Expression.Call, assign back to the original parameter:

var errorExp = Expression.Parameter(typeof(string).MakeByRefType()); //TryGetTableInfoCall->out string error

var innerOutVariable =
    Expression.Variable(tableInfoType);
var callMethod = Expression.Call(tableInfoCacheExp,
    method,
    innerOutVariable, errorExp);
var innerOutToOuterOutVariable = Expression.Assign(p2, innerOutVariable);
var returnVariable = Expression.Variable(method.ReturnType);
var assignToReturnVariable = Expression.Assign(returnVariable, callMethod);
var block = Expression.Block(new List<ParameterExpression> {
                innerOutVariable,
                returnVariable},
assignToReturnVariable,
innerOutToOuterOutVariable,
returnVariable);

var fun = Expression.Lambda<TryGetTableInfoCall>(
block, p1, p2, errorExp);

var ret = fun.Compile().Invoke(tableInfoCache,
out tableInfo, out error);

Your modified (working) fiddle

本文标签: cExpression out parameter return nullStack Overflow