如何解决删除表达式树中的强制转换操作
我想删除表达式树中的任何强制转换表达式。我们可以假设演员表是多余的。
例如,这两个表达式:
IFoo t => ((t as Foo).Bar as IExtraBar).Baz;
IFoo t => ((IExtraBar)(t as Foo).Bar).Baz;
变成这样:
IFoo t => t.Bar.Baz
你是如何做到这一点的?
示例代码
下面的示例说明了一个非常简单的场景。但是,我的编码失败并出现异常:
未处理的异常:System.ArgumentException:未为类型“ExpressionTest.Program+IFoo”定义属性“IBar Bar”
using System;
using System.Linq.Expressions;
namespace ExpressionTest
{
class Program
{
public interface IBar
{
string Baz { get; }
}
public interface IExtraBar : IBar
{
}
public interface IFoo
{
IBar Bar { get; }
}
public class Foo : IFoo
{
public IBar Bar { get; }
}
static void Main(string[] args)
{
Expression<Func<IFoo,string>> expr = t => ((t as Foo).Bar as IExtraBar).Baz;
Expression<Func<IFoo,string>> expr2 = t => ((IExtraBar)(t as Foo).Bar).Baz;
// Wanted: IFoo t => t.Bar.Baz
var visitor = new CastRemoverVisitor();
visitor.Visit(expr);
Console.WriteLine(visitor.Expression.ToString());
}
public class CastRemoverVisitor : ExpressionVisitor
{
public Expression Expression { get; private set; }
public override Expression Visit(Expression node)
{
Expression ??= node;
return base.Visit(node);
}
protected override Expression VisitUnary(UnaryExpression node)
{
Expression = node.Operand;
return Visit(node.Operand);
}
}
}
}
最终解决方案
接受的答案指出了 Expression.MakeMemberAccess 的使用和一些界面技巧。我们可以稍微“改进”代码,使用接口 PropertyInfo
而不是通过接口 getter。我最终得到以下结果:
public class CastRemoverVisitor : ExpressionVisitor
{
protected override Expression VisitUnary(UnaryExpression node)
{
return node.IsCastExpression() ? Visit(node.Operand) : base.VisitUnary(node);
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Expression is UnaryExpression unaryExpression &&
unaryExpression.IsCastExpression())
{
var propertyInfo = node.Member.ToInterfacedproperty();
if (propertyInfo != null)
{
return base.Visit(
Expression.MakeMemberAccess(
unaryExpression.Operand,propertyInfo
));
}
}
return base.VisitMember(node);
}
}
// And some useful extension methods...
public static class MemberInfoExtensions
{
public static MemberInfo ToInterfacedProperty(this MemberInfo member)
{
var interfaces = member.DeclaringType!.GetInterfaces();
var mi = interfaces.Select(i => i.GetProperty(member.Name))
.FirstOrDefault(p => p != null);
return mi;
}
}
public static class ExpressionExtensions
{
public static bool IsCastExpression(this Expression expression) =>
expression.NodeType == ExpressionType.TypeAs ||
expression.NodeType == ExpressionType.Convert;
}
然后我们像这样使用它:
var visitor = new CastRemoverVisitor();
var cleanExpr = visitor.Visit(expr);
Console.WriteLine(cleanExpr.ToString());
解决方法
首先,不错的再现。谢谢。
问题是属性访问 (t as Foo).Bar
正在调用 Foo.Bar
的 getter,而不是 IFoo.Bar
的 getter(是的,这些是具有不同 MethodInfo 的不同事物)。>
您可以通过覆盖 VisitMember
看到这一点,并看到 MethodInfo
被传递。
但是,这样的方法似乎有效。我们必须在成员访问点解开一些东西,因为我们只有在找到一个等效的成员来访问非强制类型时才能继续:
public class CastRemoverVisitor : ExpressionVisitor
{
protected override Expression VisitMember(MemberExpression node)
{
if (node.Expression is UnaryExpression { NodeType: ExpressionType.TypeAs or ExpressionType.Convert,Operand: var operand } &&
node.Member is PropertyInfo propertyInfo &&
operand.Type.IsInterface)
{
// Is this just inheriting a type from a base interface?
// Get rid of the cast,and just call the property on the uncasted member
if (propertyInfo.DeclaringType == operand.Type)
{
return base.Visit(Expression.MakeMemberAccess(operand,propertyInfo));
}
// Is node.Expression a concrete type,which implements this interface method?
var methodInfo = GetInterfaceMethodInfo(operand.Type,node.Expression.Type,propertyInfo.GetMethod);
if (methodInfo != null)
{
return base.Visit(Expression.Call(operand,methodInfo));
}
}
return base.VisitMember(node);
}
private static MethodInfo GetInterfaceMethodInfo(Type interfaceType,Type implementationType,MethodInfo implementationMethodInfo)
{
if (!implementationType.IsClass)
return null;
var map = implementationType.GetInterfaceMap(interfaceType);
for (int i = 0; i < map.InterfaceMethods.Length; i++)
{
if (map.TargetMethods[i] == implementationMethodInfo)
{
return map.InterfaceMethods[i];
}
}
return null;
}
}
我确信有些情况会打破这一点(我想到了字段,我知道 GetInterfaceMap
在某些情况下与泛型不能很好地配合),但这是一个起点。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。