提交 5450788b 编写于 作者: D dotnet-bot

Add System.Reflection.DispatchProxy library

Initial commit of System.Reflection.DispatchProxy src and tests.
DispatchProxy provides a mechanism to dynamcially create proxy types
that implement a specified interface and derive from a specified
DispatchProxy type.  Method invocations on the generated proxy
instance are dispatched to that DispatchProxy base type.


Commit migrated from https://github.com/dotnet/corefx/commit/ca637be1114f4dc7d06321fe6f1d1d15db252ae7
上级 eb6651fb

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.22609.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Reflection.DispatchProxy", "src\System.Reflection.DispatchProxy.csproj", "{1E689C1B-690C-4799-BDE9-6E7990585894}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{20F85A9C-FC0B-4220-B11F-38205E6F67F8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Reflection.DispatchProxy.Tests", "tests\System.Reflection.DispatchProxy.Tests.csproj", "{089444FE-8FF5-4D8F-A51B-32D026425F6B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1E689C1B-690C-4799-BDE9-6E7990585894}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1E689C1B-690C-4799-BDE9-6E7990585894}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1E689C1B-690C-4799-BDE9-6E7990585894}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1E689C1B-690C-4799-BDE9-6E7990585894}.Release|Any CPU.Build.0 = Release|Any CPU
{089444FE-8FF5-4D8F-A51B-32D026425F6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{089444FE-8FF5-4D8F-A51B-32D026425F6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{089444FE-8FF5-4D8F-A51B-32D026425F6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{089444FE-8FF5-4D8F-A51B-32D026425F6B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{089444FE-8FF5-4D8F-A51B-32D026425F6B} = {20F85A9C-FC0B-4220-B11F-38205E6F67F8}
EndGlobalSection
EndGlobal
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="BaseType_Cannot_Be_Sealed" xml:space="preserve">
<value>The base type '{0}' cannot be sealed.</value>
</data>
<data name="BaseType_Must_Have_Default_Ctor" xml:space="preserve">
<value>The base type '{0}' must have a public parameterless constructor.</value>
</data>
<data name="InterfaceType_Must_Be_Interface" xml:space="preserve">
<value>The type '{0}' must be an interface, not a class.</value>
</data>
<data name="BaseType_Cannot_Be_Abstract" xml:space="preserve">
<value>The base type '{0}' cannot be abstract.</value>
</data>
</root>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<PropertyGroup>
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">AnyCPU</Platform>
<ProjectGuid>{1E689C1B-690C-4799-BDE9-6E7990585894}</ProjectGuid>
<OutputType>Library</OutputType>
<AssemblyName>System.Reflection.DispatchProxy</AssemblyName>
<AssemblyVersion>4.0.0.0</AssemblyVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<!-- Default configurations to help VS understand the configurations -->
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup>
<!-- References are resolved from packages.config -->
<ItemGroup>
<Compile Include="System\Reflection\DispatchProxy.cs" />
<Compile Include="System\Reflection\DispatchProxyGenerator.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>
\ No newline at end of file
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Reflection;
namespace System.Reflection
{
/// <summary>
/// DispatchProxy provides a mechanism for the instantiation of proxy objects and handling of
/// their method dispatch.
/// </summary>
public abstract class DispatchProxy
{
protected DispatchProxy()
{
}
/// <summary>
/// Whenever any method on the generated proxy type is called, this method
/// will be invoked to dispatch control.
/// </summary>
/// <param name="targetMethod">The method the caller invoked</param>
/// <param name="args">The arguments the caller passed to the method</param>
/// <returns>The object to return to the caller, or <c>null</c> for void methods</returns>
protected abstract object Invoke(MethodInfo targetMethod, object[] args);
/// <summary>
/// Creates an object instance that derives from class <typeparamref name="TProxy"/>
/// and implements interface <typeparamref name="T"/>.
/// </summary>
/// <typeparam name="T">The interface the proxy should implement.</typeparam>
/// <typeparam name="TProxy">The base class to use for the proxy class.</typeparam>
/// <returns>An object instance that implements <typeparamref name="T"/>.</returns>
/// <exception cref="System.ArgumentException"><typeparamref name="T"/> is a class,
/// or <typeparamref name="TProxy"/> is sealed or does not have a parameterless constructor</exception>
public static T Create<T, TProxy>()
where TProxy : DispatchProxy
{
return (T)DispatchProxyGenerator.CreateProxyInstance(typeof(TProxy), typeof(T));
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="System.Collections" version="4.0.10-beta-22703" />
<package id="System.Diagnostics.Debug" version="4.0.10-beta-22703" />
<package id="System.Linq" version="4.0.0-beta-22703" />
<package id="System.Reflection" version="4.0.10-beta-22703" />
<package id="System.Reflection.Emit" version="4.0.0-beta-22703" />
<package id="System.Reflection.Emit.ILGeneration" version="4.0.0-beta-22703" />
<package id="System.Reflection.Extensions" version="4.0.0-beta-22703" />
<package id="System.Reflection.Primitives" version="4.0.0-beta-22703" />
<package id="System.Resources.ResourceManager" version="4.0.0-beta-22703" />
<package id="System.Runtime" version="4.0.20-beta-22703" />
<package id="System.Runtime.Extensions" version="4.0.10-beta-22703" />
<package id="System.Threading" version="4.0.10-beta-22703" />
</packages>
\ No newline at end of file
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace DispatchProxyTests
{
public static class DispatchProxyTests
{
[Fact]
public static void Create_Proxy_Derives_From_DispatchProxy_BaseType()
{
TestType_IHelloService proxy = DispatchProxy.Create<TestType_IHelloService, TestDispatchProxy>();
Assert.NotNull(proxy);
Assert.True(typeof(TestDispatchProxy).GetTypeInfo().IsAssignableFrom(proxy.GetType().GetTypeInfo()),
String.Format("Proxy type {0} did not derive from {1}", proxy.GetType().Name, typeof(TestDispatchProxy).Name));
}
[Fact]
public static void Create_Proxy_Implements_All_Interfaces()
{
TestType_IHelloAndGoodbyeService proxy = DispatchProxy.Create<TestType_IHelloAndGoodbyeService, TestDispatchProxy>();
Assert.NotNull(proxy);
Type[] implementedInterfaces = typeof(TestType_IHelloAndGoodbyeService).GetTypeInfo().ImplementedInterfaces.ToArray();
foreach (Type t in implementedInterfaces)
{
Assert.True(t.GetTypeInfo().IsAssignableFrom(proxy.GetType().GetTypeInfo()),
String.Format("Proxy type {0} did not derive from {1}", proxy.GetType().Name, t.Name));
}
}
[Fact]
public static void Create_Same_Proxy_Type_And_Base_Type_Reuses_Same_Generated_Type()
{
TestType_IHelloService proxy1 = DispatchProxy.Create<TestType_IHelloService, TestDispatchProxy>();
TestType_IHelloService proxy2 = DispatchProxy.Create<TestType_IHelloService, TestDispatchProxy>();
Assert.NotNull(proxy1);
Assert.NotNull(proxy2);
Assert.True(proxy1.GetType() == proxy2.GetType(),
String.Format("First instance of proxy type was {0} but second was {1}", proxy1.GetType().Name, proxy2.GetType().Name));
}
[Fact]
public static void Create_Proxy_Instances_Of_Same_Proxy_And_Base_Type_Are_Unique()
{
TestType_IHelloService proxy1 = DispatchProxy.Create<TestType_IHelloService, TestDispatchProxy>();
TestType_IHelloService proxy2 = DispatchProxy.Create<TestType_IHelloService, TestDispatchProxy>();
Assert.NotNull(proxy1);
Assert.NotNull(proxy2);
Assert.False(object.ReferenceEquals(proxy1, proxy2),
String.Format("First and second instance of proxy type {0} were the same instance", proxy1.GetType().Name));
}
[Fact]
public static void Create_Same_Proxy_Type_With_Different_BaseType_Uses_Different_Generated_Type()
{
TestType_IHelloService proxy1 = DispatchProxy.Create<TestType_IHelloService, TestDispatchProxy>();
TestType_IHelloService proxy2 = DispatchProxy.Create<TestType_IHelloService, TestDispatchProxy2>();
Assert.NotNull(proxy1);
Assert.NotNull(proxy2);
Assert.False(proxy1.GetType() == proxy2.GetType(),
String.Format("Proxy generated for base type {0} used same for base type {1}", typeof(TestDispatchProxy).Name, typeof(TestDispatchProxy).Name));
}
[Fact]
public static void Created_Proxy_With_Different_Proxy_Type_Use_Different_Generated_Type()
{
TestType_IHelloService proxy1 = DispatchProxy.Create<TestType_IHelloService, TestDispatchProxy>();
TestType_IGoodbyeService proxy2 = DispatchProxy.Create<TestType_IGoodbyeService, TestDispatchProxy>();
Assert.NotNull(proxy1);
Assert.NotNull(proxy2);
Assert.False(proxy1.GetType() == proxy2.GetType(),
String.Format("Proxy generated for type {0} used same for type {1}", typeof(TestType_IHelloService).Name, typeof(TestType_IGoodbyeService).Name));
}
[Fact]
public static void Create_Using_Concrete_Proxy_Type_Throws_ArgumentException()
{
ArgumentException caughtException = null;
try
{
TestType_ConcreteClass proxy = DispatchProxy.Create<TestType_ConcreteClass, TestDispatchProxy>();
}
catch (ArgumentException ex)
{
caughtException = ex;
}
catch (Exception e)
{
Assert.True(false, String.Format("Caught unexpected exception {0}", e.ToString()));
}
Assert.True(caughtException != null, "Expected ArgumentException to be thrown");
Assert.True(String.Equals(caughtException.ParamName, "T"),
String.Format("Expected paramName 'T' but received '{0}'", caughtException.ParamName));
}
[Fact]
public static void Create_Using_Sealed_BaseType_Throws_ArgumentException()
{
ArgumentException caughtException = null;
try
{
TestType_IHelloService proxy = DispatchProxy.Create<TestType_IHelloService, Sealed_TestDispatchProxy>();
}
catch (ArgumentException ex)
{
caughtException = ex;
}
catch (Exception e)
{
Assert.True(false, String.Format("Caught unexpected exception {0}", e.ToString()));
}
Assert.True(caughtException != null, "Expected ArgumentException to be thrown");
Assert.True(String.Equals(caughtException.ParamName, "TProxy"),
String.Format("Expected paramName 'TProxy' but received '{0}'", caughtException.ParamName));
}
[Fact]
public static void Create_Using_Abstract_BaseType_Throws_ArgumentException()
{
ArgumentException caughtException = null;
try
{
TestType_IHelloService proxy = DispatchProxy.Create<TestType_IHelloService, Abstract_TestDispatchProxy>();
}
catch (ArgumentException ex)
{
caughtException = ex;
}
catch (Exception e)
{
Assert.True(false, String.Format("Caught unexpected exception {0}", e.ToString()));
}
Assert.True(caughtException != null, "Expected ArgumentException to be thrown");
Assert.True(String.Equals(caughtException.ParamName, "TProxy"),
String.Format("Expected paramName 'TProxy' but received '{0}'", caughtException.ParamName));
}
[Fact]
public static void Create_Using_BaseType_Without_Default_Ctor_Throws_ArgumentException()
{
ArgumentException caughtException = null;
try
{
TestType_IHelloService proxy = DispatchProxy.Create<TestType_IHelloService, NoDefaultCtor_TestDispatchProxy>();
}
catch (ArgumentException ex)
{
caughtException = ex;
}
catch (Exception e)
{
Assert.True(false, String.Format("Caught unexpected exception {0}", e.ToString()));
}
Assert.True(caughtException != null, "Expected ArgumentException to be thrown");
Assert.True(String.Equals(caughtException.ParamName, "TProxy"),
String.Format("Expected paramName 'TProxy' but received '{0}'", caughtException.ParamName));
}
[Fact]
public static void Invoke_Receives_Correct_MethodInfo_And_Arguments()
{
bool wasInvoked = false;
StringBuilder errorBuilder = new StringBuilder();
// This Func is called whenever we call a method on the proxy.
// This is where we validate it received the correct arguments and methods
Func<MethodInfo, object[], object> invokeCallback = (method, args) =>
{
wasInvoked = true;
if (method == null)
{
string error = String.Format("Proxy for {0} was called with null method", typeof(TestType_IHelloService).Name);
errorBuilder.AppendLine(error);
return null;
}
else
{
MethodInfo expectedMethod = typeof(TestType_IHelloService).GetTypeInfo().GetDeclaredMethod("Hello");
if (expectedMethod != method)
{
string error = String.Format("Proxy for {0} was called with incorrect method. Expected = {1}, Actual = {2}",
typeof(TestType_IHelloService).Name, expectedMethod, method);
errorBuilder.AppendLine(error);
return null;
}
}
return "success";
};
TestType_IHelloService proxy = DispatchProxy.Create<TestType_IHelloService, TestDispatchProxy>();
Assert.NotNull(proxy);
TestDispatchProxy dispatchProxy = proxy as TestDispatchProxy;
Assert.NotNull(dispatchProxy);
// Redirect Invoke to our own Func above
dispatchProxy.CallOnInvoke = invokeCallback;
// Calling this method now will invoke the Func above which validates correct method
proxy.Hello("testInput");
Assert.True(wasInvoked, "The invoke method was not called");
Assert.True(errorBuilder.Length == 0, errorBuilder.ToString());
}
[Fact]
public static void Invoke_Receives_Correct_MethodInfo()
{
MethodInfo invokedMethod = null;
TestType_IHelloService proxy = DispatchProxy.Create<TestType_IHelloService, TestDispatchProxy>();
((TestDispatchProxy)proxy).CallOnInvoke = (method, args) =>
{
invokedMethod = method;
return String.Empty;
};
proxy.Hello("testInput");
MethodInfo expectedMethod = typeof(TestType_IHelloService).GetTypeInfo().GetDeclaredMethod("Hello");
Assert.True(invokedMethod != null && expectedMethod == invokedMethod, String.Format("Invoke expected method {0} but actual was {1}", expectedMethod, invokedMethod));
}
[Fact]
public static void Invoke_Receives_Correct_Arguments()
{
object[] actualArgs = null;
TestType_IHelloService proxy = DispatchProxy.Create<TestType_IHelloService, TestDispatchProxy>();
((TestDispatchProxy)proxy).CallOnInvoke = (method, args) =>
{
actualArgs = args;
return String.Empty;
};
proxy.Hello("testInput");
object[] expectedArgs = new object[] { "testInput" };
Assert.True(actualArgs != null && actualArgs.Length == expectedArgs.Length,
String.Format("Invoked expected object[] of length {0} but actual was {1}",
expectedArgs.Length, (actualArgs == null ? "null" : actualArgs.Length.ToString())));
for (int i = 0; i < expectedArgs.Length; ++i)
{
Assert.True(expectedArgs[i].Equals(actualArgs[i]),
String.Format("Expected arg[{0}] = '{1}' but actual was '{2}'",
i, expectedArgs[i], actualArgs[i]));
}
}
[Fact]
public static void Invoke_Returns_Correct_Value()
{
TestType_IHelloService proxy = DispatchProxy.Create<TestType_IHelloService, TestDispatchProxy>();
((TestDispatchProxy)proxy).CallOnInvoke = (method, args) =>
{
return "testReturn";
};
string expectedResult = "testReturn";
string actualResult = proxy.Hello(expectedResult);
Assert.Equal(expectedResult, actualResult);
}
[Fact]
public static void Invoke_Multiple_Parameters_Receives_Correct_Arguments()
{
object[] invokedArgs = null;
object[] expectedArgs = new object[] { (int)42, "testString", (double)5.0 };
TestType_IMultipleParameterService proxy = DispatchProxy.Create<TestType_IMultipleParameterService, TestDispatchProxy>();
((TestDispatchProxy)proxy).CallOnInvoke = (method, args) =>
{
invokedArgs = args;
return 0.0;
};
proxy.TestMethod((int)expectedArgs[0], (string)expectedArgs[1], (double)expectedArgs[2]);
Assert.True(invokedArgs != null && invokedArgs.Length == expectedArgs.Length,
String.Format("Expected {0} arguments but actual was {1}",
expectedArgs.Length, invokedArgs == null ? "null" : invokedArgs.Length.ToString()));
for (int i = 0; i < expectedArgs.Length; ++i)
{
Assert.True(expectedArgs[i].Equals(invokedArgs[i]),
String.Format("Expected arg[{0}] = '{1}' but actual was '{2}'",
i, expectedArgs[i], invokedArgs[i]));
}
}
[Fact]
public static void Invoke_Multiple_Parameters_Via_Params_Receives_Correct_Arguments()
{
object[] actualArgs = null;
object[] invokedArgs = null;
object[] expectedArgs = new object[] { 42, "testString", 5.0 };
TestType_IMultipleParameterService proxy = DispatchProxy.Create<TestType_IMultipleParameterService, TestDispatchProxy>();
((TestDispatchProxy)proxy).CallOnInvoke = (method, args) =>
{
invokedArgs = args;
return String.Empty;
};
proxy.ParamsMethod((int)expectedArgs[0], (string)expectedArgs[1], (double)expectedArgs[2]);
// All separate params should have become a single object[1] array
Assert.True(invokedArgs != null && invokedArgs.Length == 1,
String.Format("Expected single element object[] but actual was {0}",
invokedArgs == null ? "null" : invokedArgs.Length.ToString()));
// That object[1] should contain an object[3] containing the args
actualArgs = invokedArgs[0] as object[];
Assert.True(actualArgs != null && actualArgs.Length == expectedArgs.Length,
String.Format("Invoked expected object[] of length {0} but actual was {1}",
expectedArgs.Length, (actualArgs == null ? "null" : actualArgs.Length.ToString())));
for (int i = 0; i < expectedArgs.Length; ++i)
{
Assert.True(expectedArgs[i].Equals(actualArgs[i]),
String.Format("Expected arg[{0}] = '{1}' but actual was '{2}'",
i, expectedArgs[i], actualArgs[i]));
}
}
[Fact]
public static void Invoke_Void_Returning_Method_Accepts_Null_Return()
{
MethodInfo invokedMethod = null;
TestType_IOneWay proxy = DispatchProxy.Create<TestType_IOneWay, TestDispatchProxy>();
((TestDispatchProxy)proxy).CallOnInvoke = (method, args) =>
{
invokedMethod = method;
return null;
};
proxy.OneWay();
MethodInfo expectedMethod = typeof(TestType_IOneWay).GetTypeInfo().GetDeclaredMethod("OneWay");
Assert.True(invokedMethod != null && expectedMethod == invokedMethod, String.Format("Invoke expected method {0} but actual was {1}", expectedMethod, invokedMethod));
}
[Fact]
public static void Invoke_Same_Method_Multiple_Interfaces_Calls_Correct_Method()
{
List<MethodInfo> invokedMethods = new List<MethodInfo>();
TestType_IHelloService1And2 proxy = DispatchProxy.Create<TestType_IHelloService1And2, TestDispatchProxy>();
((TestDispatchProxy)proxy).CallOnInvoke = (method, args) =>
{
invokedMethods.Add(method);
return null;
};
((TestType_IHelloService)proxy).Hello("calling 1");
((TestType_IHelloService2)proxy).Hello("calling 2");
Assert.True(invokedMethods.Count == 2, String.Format("Expected 2 method invocations but received {0}", invokedMethods.Count));
MethodInfo expectedMethod = typeof(TestType_IHelloService).GetTypeInfo().GetDeclaredMethod("Hello");
Assert.True(invokedMethods[0] != null && expectedMethod == invokedMethods[0], String.Format("First invoke should have been TestType_IHelloService.Hello but actual was {0}", invokedMethods[0]));
expectedMethod = typeof(TestType_IHelloService2).GetTypeInfo().GetDeclaredMethod("Hello");
Assert.True(invokedMethods[1] != null && expectedMethod == invokedMethods[1], String.Format("Second invoke should have been TestType_IHelloService2.Hello but actual was {0}", invokedMethods[1]));
}
[Fact]
public static void Invoke_Thrown_Exception_Rethrown_To_Caller()
{
Exception actualException = null;
InvalidOperationException expectedException = new InvalidOperationException("testException");
TestType_IHelloService proxy = DispatchProxy.Create<TestType_IHelloService, TestDispatchProxy>();
((TestDispatchProxy)proxy).CallOnInvoke = (method, args) =>
{
throw expectedException;
};
try
{
proxy.Hello("testCall");
}
catch (Exception e)
{
actualException = e;
}
Assert.Equal(expectedException, actualException);
}
[Fact]
public static void Invoke_Property_Setter_And_Getter_Invokes_Correct_Methods()
{
List<MethodInfo> invokedMethods = new List<MethodInfo>();
TestType_IPropertyService proxy = DispatchProxy.Create<TestType_IPropertyService, TestDispatchProxy>();
((TestDispatchProxy)proxy).CallOnInvoke = (method, args) =>
{
invokedMethods.Add(method);
return null;
};
proxy.ReadWrite = "testValue";
string actualValue = proxy.ReadWrite;
Assert.True(invokedMethods.Count == 2, String.Format("Expected 2 method invocations but received {0}", invokedMethods.Count));
PropertyInfo propertyInfo = typeof(TestType_IPropertyService).GetTypeInfo().GetDeclaredProperty("ReadWrite");
Assert.NotNull(propertyInfo);
MethodInfo expectedMethod = propertyInfo.SetMethod;
Assert.True(invokedMethods[0] != null && expectedMethod == invokedMethods[0], String.Format("First invoke should have been {0} but actual was {1}",
expectedMethod.Name, invokedMethods[0]));
expectedMethod = propertyInfo.GetMethod;
Assert.True(invokedMethods[1] != null && expectedMethod == invokedMethods[1], String.Format("First invoke should have been {0} but actual was {1}",
expectedMethod.Name, invokedMethods[1]));
Assert.Null(actualValue);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{089444FE-8FF5-4D8F-A51B-32D026425F6B}</ProjectGuid>
<OutputType>Library</OutputType>
<AssemblyName>System.Reflection.DispatchProxy.Tests</AssemblyName>
<RootNamespace>System.Reflection.DispatchProxy.Tests</RootNamespace>
</PropertyGroup>
<!-- Default configurations to help VS understand the configurations -->
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup>
<ItemGroup>
<Compile Include="DispatchProxyTests.cs" />
<Compile Include="TestTypes.cs" />
</ItemGroup>
<!-- References are resolved from packages.config -->
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\src\System.Reflection.DispatchProxy.csproj">
<Project>{1e689c1b-690c-4799-bde9-6e7990585894}</Project>
<Name>System.Reflection.DispatchProxy</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>
\ No newline at end of file
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
// Test types used to make proxies.
public interface TestType_IHelloService
{
string Hello(string message);
}
public interface TestType_IGoodbyeService
{
string Goodbye(string message);
}
// Demonstrates interface implementing multiple other interfaces
public interface TestType_IHelloAndGoodbyeService : TestType_IHelloService, TestType_IGoodbyeService
{
}
// Deliberately contains method with same signature of TestType_IHelloService (see TestType_IHelloService1And2).
public interface TestType_IHelloService2
{
string Hello(string message);
}
// Demonstrates 2 interfaces containing same method name dispatches to the right one
public interface TestType_IHelloService1And2 : TestType_IHelloService, TestType_IHelloService2
{
}
// Demonstrates methods taking multiple parameters as well as a params parameter
public interface TestType_IMultipleParameterService
{
double TestMethod(int i, string s, double d);
object ParamsMethod(params object[] parameters);
}
// Demonstrate a void-returning method and parameterless method
public interface TestType_IOneWay
{
void OneWay();
}
// Demonstrates proxies can be made for properties.
public interface TestType_IPropertyService
{
string ReadWrite { get; set; }
}
// Negative -- demonstrates trying to use a class for the interface type for the proxy
public class TestType_ConcreteClass
{
public string Echo(string s) { return null; }
}
// Negative -- demonstrates base type that is sealed and should generate exception
public sealed class Sealed_TestDispatchProxy : DispatchProxy
{
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
throw new InvalidOperationException();
}
}
// This test double creates a proxy instance for the requested 'ProxyT' type.
// When methods are invoked on that proxy, it will call a registered callback.
public class TestDispatchProxy : DispatchProxy
{
// Gets or sets the Action to invoke when clients call methods on the proxy.
public Func<MethodInfo, object[], object> CallOnInvoke { get; set; }
// Gets the proxy itself (which is always 'this')
public object GetProxy()
{
return this;
}
// Implementation of DispatchProxy.Invoke() just calls back to given Action
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
return CallOnInvoke(targetMethod, args);
}
}
public class TestDispatchProxy2 : TestDispatchProxy
{
}
// Negative test -- demonstrates base type that is abstract
public abstract class Abstract_TestDispatchProxy : DispatchProxy
{
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
throw new InvalidOperationException();
}
}
// Negative -- demonstrates base type that has no public default ctor
public class NoDefaultCtor_TestDispatchProxy : DispatchProxy
{
private NoDefaultCtor_TestDispatchProxy()
{
}
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
throw new InvalidOperationException();
}
}
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="System.Collections" version="4.0.10-beta-22703" />
<package id="System.Diagnostics.Debug" version="4.0.10-beta-22703" />
<package id="System.Linq" version="4.0.0-beta-22703" />
<package id="System.Reflection" version="4.0.10-beta-22703" />
<package id="System.Reflection.Emit" version="4.0.0-beta-22703" />
<package id="System.Reflection.Emit.ILGeneration" version="4.0.0-beta-22703" />
<package id="System.Reflection.Extensions" version="4.0.0-beta-22703" />
<package id="System.Reflection.Primitives" version="4.0.0-beta-22703" />
<package id="System.Resources.ResourceManager" version="4.0.0-beta-22703" />
<package id="System.Runtime" version="4.0.20-beta-22703" />
<package id="System.Runtime.Extensions" version="4.0.10-beta-22703" />
<package id="System.Threading" version="4.0.10-beta-22703" />
<package id="xunit" version="2.0.0-beta5-build2785" />
<package id="xunit.abstractions.netcore" version="1.0.0-prerelease" />
<package id="xunit.assert" version="2.0.0-beta5-build2785" />
<package id="xunit.core.netcore" version="1.0.0-prerelease" />
</packages>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册