Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/wix/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 我可以将反射用于RealProxy实例吗?_C#_Reflection_Clr_Proxy Classes - Fatal编程技术网

C# 我可以将反射用于RealProxy实例吗?

C# 我可以将反射用于RealProxy实例吗?,c#,reflection,clr,proxy-classes,C#,Reflection,Clr,Proxy Classes,我很确定我遗漏了一些约束或警告,但我的情况是这样的。假设我有一个类,我希望有一个代理,如下所示: public class MyList : MarshalByRefObject, IList<string> { private List<string> innerList; public MyList(IEnumerable<string> stringList) { this.innerList = new Lis

我很确定我遗漏了一些约束或警告,但我的情况是这样的。假设我有一个类,我希望有一个代理,如下所示:

public class MyList : MarshalByRefObject, IList<string>
{
    private List<string> innerList;

    public MyList(IEnumerable<string> stringList)
    {
        this.innerList = new List<string>(stringList);
    }

    // IList<string> implementation omitted for brevity.
    // For the sake of this exercise, assume each method
    // implementation merely passes through to the associated
    // method on the innerList member variable.
}
现在,如果我尝试使用反射来访问代理对象,我会遇到问题。以下是一个例子:

class Program
{
    static void Main(string[] args)
    {
        ListConsumer listConsumer = new ListConsumer();

        // These calls merely illustrate that the property can be
        // properly accessed and methods called through the created
        // proxy without issue.
        Console.WriteLine("List contains {0} items", listConsumer.MyList.Count);
        Console.WriteLine("List contents:");
        foreach(string stringValue in listConsumer.MyList)
        {
            Console.WriteLine(stringValue);
        }

        Type listType = listConsumer.MyList.GetType();
        foreach (Type interfaceType in listType.GetInterfaces())
        {
            if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(ICollection<>))
            {
                // Attempting to get the value of the Count property via
                // reflection throws an exception.
                Console.WriteLine("Checking interface {0}", interfaceType.Name);
                System.Reflection.PropertyInfo propInfo = interfaceType.GetProperty("Count");
                int count = (int)propInfo.GetValue(listConsumer.MyList, null);
            }
            else
            {
                Console.WriteLine("Skipping interface {0}", interfaceType.Name);
            }
        }

        Console.ReadLine();
    }
}
类程序
{
静态void Main(字符串[]参数)
{
ListConsumer ListConsumer=新ListConsumer();
//这些调用仅说明属性可以
//通过已创建的
//没有问题的代理。
WriteLine(“列表包含{0}项”,listcummer.MyList.Count);
Console.WriteLine(“列表内容:”);
foreach(listConsumer.MyList中的字符串stringValue)
{
Console.WriteLine(stringValue);
}
类型listType=listConsumer.MyList.GetType();
foreach(在listType.GetInterfaces()中键入interfaceType)
{
if(interfaceType.IsGenericType&&interfaceType.GetGenericTypeDefinition()==typeof(ICollection))
{
//正在尝试通过获取Count属性的值
//反射抛出一个异常。
WriteLine(“检查接口{0}”,interfaceType.Name);
System.Reflection.PropertyInfo-propInfo=interfaceType.GetProperty(“计数”);
int count=(int)propInfo.GetValue(listConsumer.MyList,null);
}
其他的
{
WriteLine(“跳过接口{0}”,interfaceType.Name);
}
}
Console.ReadLine();
}
}
试图通过反射调用
Count
属性上的
GetValue
会引发以下异常:

中发生“System.Reflection.TargetException”类型的异常 mscorlib.dll,但未在用户代码中处理

其他信息:对象与目标类型不匹配

当尝试获取
Count
属性的值时,显然框架正在调用
System.Runtime.InteropServices.WindowsRuntime.IVector
来调用
get\u Size
方法。我不明白这个调用是如何在代理的底层对象(实际列表)上失败的。如果我没有使用对象的代理,那么通过反射获取属性值就可以了。我做错了什么?我能做我想做的事吗


编辑:A关于Microsoft Connect站点的此问题。

我认为这可能是.Net framework中的一个错误。不知何故,
RuntimePropertyInfo.GetValue
方法为
ICollection.Count
属性选择了错误的实现,它似乎与WindowsRuntime投影有关。当他们将WindowsRuntime互操作放在框架中时,远程处理代码可能是重做的

我将框架切换到target.NET2.0,因为我认为如果这是一个bug,它不应该在该框架中。转换时,Visual Studio删除了对我的console exe项目的“首选32位”检查(因为这在2.0中不存在)。当不存在时,它会毫无例外地运行

总之,它在.NET2.0上以32位和64位运行。它在64位的.NET4.x上运行。该异常仅在.Net 4.x 32位上引发。这看起来真像个虫子。如果您可以64位运行它,这将是一个解决方法

请注意,我已经安装了.NET4.6,它取代了大部分.NETFrameworkV4.x。这可能是问题的根源所在;在我得到一台没有.NET4.6的机器之前,我无法进行测试

更新:2015-09-08

在只安装了.NET4.5.2(no.4.6)的机器上也会发生这种情况

更新:2015-09-07

这是一个较小的复制,使用相同的类:

static void Main(string[] args)
{
    var myList = MyListProxy.CreateProxy(new[] {"foo", "bar", "baz", "quxx"});
    var listType = myList.GetType();
    var interfaceType = listType.GetInterface("System.Collections.Generic.ICollection`1");
    var propInfo = interfaceType.GetProperty("Count");

    // TargetException thrown on 32-bit .Net 4.5.2+ installed
    int count = (int)propInfo.GetValue(myList, null); 
}
我还尝试了
IsReadOnly
属性,但它似乎有效(没有例外)


至于bug的来源,在属性周围有两层间接寻址,一层是远程处理,另一层是元数据结构的映射,称为
MethodDef
s和实际运行时方法,内部称为
MethodDesc
。通过调用
PropertyInfo.GetValue
我们将通过这些关联
MethodDesc
指针中的一个指向底层方法实现,远程处理会进行一些指针数学运算,以在通道的另一端获得正确的
MethodDesc
。CLR代码在这里非常复杂,我对
MethodTable
的内存布局没有足够的经验,它保存了远程处理使用的
MethodDesc
记录(或者它用于获取MethodTable的映射?),但是我想说,远程处理通过一些糟糕的指针数学抓住了错误的
MethodDesc
。这就是为什么我们看到一个类似但不相关(就您的程序而言)
MethodDesc
-
UInt32 get\u Size
IVector
在调用时被调用:

System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target)
System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
ConsoleApplication1.MyListProxy.Invoke(IMessage msg) Program.cs: line: 60
System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
System.Runtime.InteropServices.WindowsRuntime.IVector`1.get_Size()
System.Runtime.InteropServices.WindowsRuntime.VectorToCollectionAdapter.Count[T]()

这是一个非常有趣的CLR错误,它的一些胆量在这场灾难中表现出来。从堆栈跟踪可以看出,它正在尝试调用的Count属性

这个类相当特殊,从来没有创建过它的实例。它是.NET4.5中添加的语言投影的一部分,使WinRT接口类型看起来像.NETFramework类型。它与SZArrayHelper类非常相似,它是一个适配器类,帮助实现非泛型数组实现像
IList
这样的泛型接口类型的错觉

这里工作的接口映射是针对WinRT接口的。如MSDN文章中所述,该接口类型映射到
IList
。内部VectorToListAdapter类处理
IList
成员,VectorCollectionAdapter处理
ICollection
成员

您的代码强制CLR查找t
static void Main(string[] args)
{
    var myList = MyListProxy.CreateProxy(new[] {"foo", "bar", "baz", "quxx"});
    var listType = myList.GetType();
    var interfaceType = listType.GetInterface("System.Collections.Generic.ICollection`1");
    var propInfo = interfaceType.GetProperty("Count");

    // TargetException thrown on 32-bit .Net 4.5.2+ installed
    int count = (int)propInfo.GetValue(myList, null); 
}
System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target)
System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
ConsoleApplication1.MyListProxy.Invoke(IMessage msg) Program.cs: line: 60
System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
System.Runtime.InteropServices.WindowsRuntime.IVector`1.get_Size()
System.Runtime.InteropServices.WindowsRuntime.VectorToCollectionAdapter.Count[T]()
public class ProxyBase : RealProxy
{
    // ... stuff ...

    public static T Cast<T>(object o)
    {
        return (T)o;
    }

    public static object Create(Type interfaceType, object coreInstance, 
        IEnforce enforce, string parentNamingSequence)
    {
        var x = new ProxyBase(interfaceType, coreInstance, enforce, 
            parentNamingSequence);

        MethodInfo castMethod = typeof(ProxyBase).GetMethod(
            "Cast").MakeGenericMethod(interfaceType);

        return castMethod.Invoke(null, new object[] { x.GetTransparentProxy() });
    }

    public override IMessage Invoke(IMessage msg)
    {
        IMethodCallMessage methodCall = (IMethodCallMessage)msg;
        var method = (MethodInfo)methodCall.MethodBase;

        if(method.DeclaringType.IsGenericType
        && method.DeclaringType.GetGenericTypeDefinition().FullName.Contains(
            "System.Runtime.InteropServices.WindowsRuntime"))
        {
            Dictionary<string, string> methodMap = new Dictionary<string, string>
            {   // add problematic methods here
                { "Append", "Add" },
                { "GetAt", "get_Item" }
            };

            if(methodMap.ContainsKey(method.Name) == false)
            {
                throw new Exception("Unable to resolve '" + method.Name + "'.");
            }
            // thanks microsoft
            string correctMethod = methodMap[method.Name];
            method = m_baseInterface.GetInterfaces().Select(
                i => i.GetMethod(correctMethod)).Where(
                    mi => mi != null).FirstOrDefault();

            if(method == null)
            {
                throw new Exception("Unable to resolve '" + method.Name + 
                    "' to '" + correctMethod + "'.");
            }
        }

        try
        {
            if(m_coreInstance == null)
            {
                var errorMessage = Resource.CoreInstanceIsNull;
                WriteLogs(errorMessage, TraceEventType.Error);
                throw new NullReferenceException(errorMessage);
            }

            var args = methodCall.Args.Select(a =>
            {
                object o;

                if(RemotingServices.IsTransparentProxy(a))
                {
                    o = (RemotingServices.GetRealProxy(a) 
                        as ProxyBase).m_coreInstance;
                }
                else
                {
                    o = a;
                }

                if(method.Name == "get_Item")
                {   // perform parameter conversions here
                    if(a.GetType() == typeof(UInt32))
                    { 
                        return Convert.ToInt32(a);
                    }

                    return a;                            
                }

                return o;
            }).ToArray();
            // this is where it barfed
            var result = method.Invoke(m_coreInstance, args);
            // special handling for GetType()
            if(method.Name == "GetType")
            {
                result = m_baseInterface;
            }
            else
            {
                // special handling for interface return types
                if(method.ReturnType.IsInterface)
                {
                    result = ProxyBase.Create(method.ReturnType, result, m_enforce, m_namingSequence);
                }
            }

            return new ReturnMessage(result, args, args.Length, methodCall.LogicalCallContext, methodCall);
        }
        catch(Exception e)
        {
            WriteLogs("Exception: " + e, TraceEventType.Error);
            if(e is TargetInvocationException && e.InnerException != null)
            {
                return new ReturnMessage(e.InnerException, msg as IMethodCallMessage);
            }
            return new ReturnMessage(e, msg as IMethodCallMessage);
        }
    }

    // ... stuff ...
}
method.DeclaringType.GetGenericTypeDefinition().FullName.Contains(
        "System.Runtime.InteropServices.WindowsRuntime")