c#-从作用域”引用的类型为’SubType’的变量’x.Sub’,但未定义错误
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了c#-从作用域”引用的类型为’SubType’的变量’x.Sub’,但未定义错误,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5235字,纯文字阅读大概需要8分钟。
内容图文
检查此提琴是否存在错误:https://dotnetfiddle.net/tlz4Qg
我有两个这样的课程:
public class ParentType{
private ParentType(){}
public int Id { get; protected set; }
public SubType Sub { get; protected set; }
}
public class SubType{
private SubType(){}
public int Id { get; protected set; }
}
我将把多级匿名表达式转换为多级非匿名表达式.为了实现这一点,我有一个类似于下面提到的表达式:
x => new
{
x.Id,
Sub = new
{
x.Sub.Id
}
}
为了实现该目标,我将其转换为这样的表达式:
x => new ParentType()
{
Id = x.Id,
Sub = new SubType()
{
Id = x.Sub.Id
},
}
但是,当我调用Compile()方法时,出现以下错误:
Variable ‘x.Sub’ of type ‘SubType’ referenced from scope ” but it is not defined
这是我的访客班:
public class ReturnTypeVisitor<TIn, TOut> : ExpressionVisitor
{
private readonly Type funcToReplace;
private ParameterExpression currentParameter;
private ParameterExpression defaultParameter;
private Type currentType;
public ReturnTypeVisitor() => funcToReplace = typeof(Func<,>).MakeGenericType(typeof(TIn), typeof(object));
protected override Expression VisitNew(NewExpression node)
{
if (!node.Type.IsAnonymousType())
return base.VisitNew(node);
if (currentType == null)
currentType = typeof(TOut);
var ctor = currentType.GetPrivateConstructor();
if (ctor == null)
return base.VisitNew(node);
NewExpression expr = Expression.New(ctor);
IEnumerable<MemberBinding> bindings = node.Members.Select(x =>
{
var mi = currentType.GetProperty(x.Name);
//if the type is anonymous then I need to transform its body
if (((PropertyInfo)x).PropertyType.IsAnonymousType())
{
//This section is became unnecessary complex!
//
var property = (PropertyInfo)x;
var parentType = currentType;
var parentParameter = currentParameter;
currentType = currentType.GetProperty(property.Name).PropertyType;
currentParameter = Expression.Parameter(currentType, currentParameter.Name + "." + property.Name);
//I pass the inner anonymous expression to VisitNew and make the non-anonymous expression from it
var xOriginal = VisitNew(node.Arguments.FirstOrDefault(a => a.Type == property.PropertyType) as NewExpression);
currentType = parentType;
currentParameter = parentParameter;
return (MemberBinding)Expression.Bind(mi, xOriginal);
}
else//if type is not anonymous then simple find the property and make the memberbinding
{
var xOriginal = Expression.PropertyOrField(currentParameter, x.Name);
return (MemberBinding)Expression.Bind(mi, xOriginal);
}
});
return Expression.MemberInit(expr, bindings);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
if (typeof(T) != funcToReplace)
return base.VisitLambda(node);
defaultParameter = node.Parameters.First();
currentParameter = defaultParameter;
var body = Visit(node.Body);
return Expression.Lambda<Func<TIn, TOut>>(body, currentParameter);
}
}
并像这样使用它:
public static Expression<Func<Tin, Tout>> Transform<Tin, Tout>(this Expression<Func<Tin, object>> source)
{
var visitor = new ReturnTypeVisitor<Tin, Tout>();
var result = (Expression<Func<Tin, Tout>>)visitor.Visit(source);
return result;// result.Compile() throw the aforementioned error
}
这是我的Visitor类中使用的扩展方法:
public static ConstructorInfo GetPrivateConstructor(this Type type) =>
type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
// this hack taken from https://stackoverflow.com/a/2483054/4685428
// and https://stackoverflow.com/a/1650895/4685428
public static bool IsAnonymousType(this Type type)
{
var markedWithAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), inherit: false).Any();
var typeName = type.Name;
return markedWithAttribute
&& (typeName.StartsWith("<>") || type.Name.StartsWith("VB$"))
&& typeName.Contains("AnonymousType");
}
更新资料
这是该问题的.Net小提琴链接:https://dotnetfiddle.net/tlz4Qg
更新资料
我删除了似乎超出问题范围的多余代码.
解决方法:
问题的原因是线路
currentParameter = Expression.Parameter(currentType, currentParameter.Name + "." + property.Name);
在VisitNew方法中.
对于您的示例,它将创建一个名为“ x.Sub”的新参数,因此,如果我们用{}标记这些参数,则实际结果为
Sub = new SubType()
{
Id = {x.Sub}.Id
},
而不是预期的
Sub = new SubType()
{
Id = {x}.Sub.Id
},
通常,除非重新映射lambda表达式,否则不应创建新的ParameterExpressions.并且所有新创建的参数都应传递给Expression.Lambda调用,否则它们将被视为“未定义”.
另外请注意,访问者代码具有一些通常不成立的假设.例如
var xOriginal = Expression.PropertyOrField(currentParameter, x.Name);
在嵌套的new中将无法使用,因为在那里您需要访问x参数的成员,例如x.Sub.Id而不是x.Id.这基本上是NewExpression.Arguments中的相应表达.
使用表达式访问者处理嵌套的lambda表达式或集合类型成员和LINQ方法需要更多的状态控制.在像示例中那样转换简单的嵌套匿名新表达式时,甚至不需要ExpressionVisitor,因为可以使用以下简单的递归方法轻松实现:
public static Expression<Func<Tin, Tout>> Transform<Tin, Tout>(this Expression<Func<Tin, object>> source)
{
return Expression.Lambda<Func<Tin, Tout>>(
Transform(source.Body, typeof(Tout)),
source.Parameters);
}
static Expression Transform(Expression source, Type type)
{
if (source.Type != type && source is NewExpression newExpr && newExpr.Members.Count > 0)
{
return Expression.MemberInit(Expression.New(type), newExpr.Members
.Select(m => type.GetProperty(m.Name))
.Zip(newExpr.Arguments, (m, e) => Expression.Bind(m, Transform(e, m.PropertyType))));
}
return source;
}
内容总结
以上是互联网集市为您收集整理的c#-从作用域”引用的类型为’SubType’的变量’x.Sub’,但未定义错误全部内容,希望文章能够帮你解决c#-从作用域”引用的类型为’SubType’的变量’x.Sub’,但未定义错误所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。