├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ └── dotnet-core.yml ├── .gitignore ├── Directory.Build.props ├── ExposedObject.sln ├── ExposedObject ├── Exposed.cs ├── ExposedObject.csproj ├── ExposedObject.snk ├── MetaObject.cs ├── Properties │ └── AssemblyInfo.cs ├── PublicAPI.Shipped.txt └── PublicAPI.Unshipped.txt ├── README.md ├── TestSubjects ├── CallSiteCachingClasses.cs ├── ClassWithByRefParameters.cs ├── ClassWithHiddenMethods.cs ├── ClassWithInheritedPrivateMembers.cs ├── GenericMethodClass.cs ├── HiddenClass.cs ├── OverloadedMethodsClass.cs ├── Properties │ └── AssemblyInfo.cs ├── StaticClass.cs └── TestSubjects.csproj └── Tests ├── CallSiteCachingTest.cs ├── ClassWithByRefParametersTest.cs ├── ClassWithHiddenMethodsTest.cs ├── ClassWithInheritedPrivateMembersTest.cs ├── GenericMethodClassTest.cs ├── HiddenClassTest.cs ├── OverloadedMethodsClassTest.cs ├── Properties └── AssemblyInfo.cs ├── StaticClassTest.cs └── Tests.csproj /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CA1303: Do not pass literals as localized parameters 4 | dotnet_diagnostic.CA1303.severity = none 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | 9 | - package-ecosystem: "nuget" 10 | directory: "/" 11 | schedule: 12 | interval: "daily" 13 | 14 | - package-ecosystem: "github-actions" 15 | directory: "/" 16 | schedule: 17 | interval: "daily" 18 | -------------------------------------------------------------------------------- /.github/workflows/dotnet-core.yml: -------------------------------------------------------------------------------- 1 | name: .NET Core 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v4 12 | - name: Setup .NET Core 13 | uses: actions/setup-dotnet@v3 14 | - name: Install dependencies 15 | run: dotnet restore 16 | - name: Build 17 | run: dotnet build --configuration Release --framework net6.0 --no-restore 18 | - name: Test 19 | run: dotnet test --configuration Release --framework net6.0 --no-restore --verbosity normal 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | #ignore thumbnails created by windows 3 | Thumbs.db 4 | #Ignore files build by Visual Studio 5 | *.obj 6 | *.pdb 7 | *.user 8 | *.aps 9 | *.pch 10 | *.vspscc 11 | *_i.c 12 | *_p.c 13 | *.ncb 14 | *.suo 15 | *.tlb 16 | *.tlh 17 | *.bak 18 | *.cache 19 | *.ilk 20 | *.log 21 | [Bb]in 22 | [Dd]ebug*/ 23 | *.lib 24 | *.sbr 25 | *.vs10x 26 | *.nupkg 27 | obj/ 28 | [Rr]elease*/ 29 | _ReSharper*/ 30 | [Tt]est[Rr]esult* 31 | packages/ 32 | .nuget/*.exe 33 | .vs 34 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | latest 6 | true 7 | true 8 | true 9 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb 10 | true 11 | true 12 | AllEnabledByDefault 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ExposedObject.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28922.388 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExposedObject", "ExposedObject\ExposedObject.csproj", "{04CE6B9D-FEC0-424A-B0BB-8A4D1E48F73E}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestSubjects", "TestSubjects\TestSubjects.csproj", "{4F9CC970-A5B4-455B-9733-B4D8605DCAAE}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{2467EDA2-6977-4223-8F31-3ED726777D22}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {04CE6B9D-FEC0-424A-B0BB-8A4D1E48F73E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {04CE6B9D-FEC0-424A-B0BB-8A4D1E48F73E}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {04CE6B9D-FEC0-424A-B0BB-8A4D1E48F73E}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {04CE6B9D-FEC0-424A-B0BB-8A4D1E48F73E}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {4F9CC970-A5B4-455B-9733-B4D8605DCAAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {4F9CC970-A5B4-455B-9733-B4D8605DCAAE}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {4F9CC970-A5B4-455B-9733-B4D8605DCAAE}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {4F9CC970-A5B4-455B-9733-B4D8605DCAAE}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {2467EDA2-6977-4223-8F31-3ED726777D22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {2467EDA2-6977-4223-8F31-3ED726777D22}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {2467EDA2-6977-4223-8F31-3ED726777D22}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {2467EDA2-6977-4223-8F31-3ED726777D22}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {F0B8BB51-32B0-447E-AA53-66C23B33D7CA} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /ExposedObject/Exposed.cs: -------------------------------------------------------------------------------- 1 | // Author: 2 | // Leszek Ciesielski (skolima@gmail.com) 3 | // 4 | // (C) 2011 Cognifide 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining 7 | // a copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to 12 | // the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | using System; 27 | using System.Dynamic; 28 | using System.Linq.Expressions; 29 | 30 | namespace ExposedObject 31 | { 32 | /// 33 | /// Exposes hidden (private/protected/internal) members of an 34 | /// or through a wrapper. 35 | /// 36 | public class Exposed : DynamicObject 37 | { 38 | /// 39 | /// The that is being exposed. 40 | /// if static members of a are being exposed. 41 | /// 42 | private readonly object? value; 43 | 44 | /// 45 | /// Initializes a new instance of the class. 46 | /// Creates a new wrapper for accessing members of subject. 47 | /// 48 | /// 49 | /// The object which will have it's members exposed. 50 | /// 51 | /// 52 | /// A new wrapper around the subject. 53 | /// 54 | private Exposed(object subject) 55 | { 56 | value = subject; 57 | SubjectType = subject.GetType(); 58 | } 59 | 60 | /// 61 | /// Initializes a new instance of the class. 62 | /// Creates a new wrapper for accessing hidden static members of a . 63 | /// 64 | /// 65 | /// The which will have it's static members exposed. 66 | /// 67 | /// 68 | /// A new wrapper around a . 69 | /// 70 | private Exposed(Type type) 71 | { 72 | SubjectType = type; 73 | } 74 | 75 | /// 76 | /// Gets the of the exposed object. 77 | /// 78 | internal Type SubjectType { get; private set; } 79 | 80 | /// 81 | /// Creates a new wrapper for accessing members of subject. 82 | /// 83 | /// 84 | /// The object which will have it's members exposed. 85 | /// 86 | /// 87 | /// A new wrapper around the subject. 88 | /// 89 | public static dynamic From(object subject) 90 | { 91 | if (subject == null) 92 | { 93 | throw new ArgumentNullException(nameof(subject)); 94 | } 95 | 96 | return new Exposed(subject); 97 | } 98 | 99 | /// 100 | /// Creates a new wrapper for accessing hidden static members of a . 101 | /// 102 | /// 103 | /// The which will have it's static members exposed. 104 | /// 105 | /// 106 | /// A new wrapper around a . 107 | /// 108 | public static dynamic From(Type type) 109 | { 110 | return new Exposed(type); 111 | } 112 | 113 | /// 114 | /// Creates a new wrapper for accessing members of a new instance of . 115 | /// 116 | /// 117 | /// The of which an instance will have it's members exposed. 118 | /// 119 | /// 120 | /// A new wrapper around a new instance of . 121 | /// 122 | public static dynamic New(Type type) 123 | { 124 | var instance = Activator.CreateInstance(type); 125 | 126 | #pragma warning disable CA1508 // Avoid dead conditional code 127 | if (instance == null) 128 | #pragma warning restore CA1508 // Avoid dead conditional code 129 | { 130 | throw new ArgumentOutOfRangeException(nameof(type), "Activator.CreateInstance returned null."); 131 | } 132 | 133 | return new Exposed(instance); 134 | } 135 | 136 | /// 137 | /// Returns the responsible for binding operations performed on this object. 138 | /// 139 | /// 140 | /// The expression tree representation of the runtime value. 141 | /// 142 | /// 143 | /// The to bind this object. 144 | /// 145 | public override DynamicMetaObject GetMetaObject(Expression parameter) 146 | { 147 | return new MetaObject(parameter, this, value == null); 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /ExposedObject/ExposedObject.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0;net6.0 4 | 5 | 6 | true 7 | ExposedObject.snk 8 | true 9 | dynamic private invoke 10 | MIT 11 | https://github.com/skolima/ExposedObject 12 | Fast dynamic wrapper for accessing hidden methods and fields of .Net objects. 13 | 2.2.0 14 | 15 | 16 | 17 | 18 | 19 | enable 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ExposedObject/ExposedObject.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skolima/ExposedObject/20b683231dab58477999fe781cb814487e8528ad/ExposedObject/ExposedObject.snk -------------------------------------------------------------------------------- /ExposedObject/MetaObject.cs: -------------------------------------------------------------------------------- 1 | // Author: 2 | // Leszek Ciesielski (skolima@gmail.com) 3 | // Manuel Josupeit-Walter (info@josupeit.com) 4 | // 5 | // (C) 2013 Cognifide 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | using System; 28 | using System.Collections.Generic; 29 | using System.Dynamic; 30 | using System.Linq.Expressions; 31 | using System.Reflection; 32 | 33 | namespace ExposedObject 34 | { 35 | /// 36 | /// Represents the dynamic binding and a binding logic of an object participating in the dynamic binding. 37 | /// 38 | internal sealed class MetaObject : DynamicMetaObject 39 | { 40 | /// 41 | /// Should this bind to or instance methods and fields. 42 | /// 43 | private readonly bool isStatic; 44 | 45 | /// 46 | /// Initializes a new instance of the class. 47 | /// 48 | /// 49 | /// The expression representing this during the dynamic binding process. 50 | /// 51 | /// 52 | /// The runtime value represented by the . 53 | /// 54 | /// 55 | /// Should this MetaObject bind to or instance methods and fields. 56 | /// 57 | public MetaObject(Expression expression, object value, bool staticBind) : 58 | base(expression, BindingRestrictions.Empty, value) 59 | { 60 | isStatic = staticBind; 61 | } 62 | 63 | /// 64 | /// Performs the binding of the dynamic invoke member operation. 65 | /// 66 | /// 67 | /// An instance of the that represents the details of the dynamic operation. 68 | /// 69 | /// 70 | /// An array of instances - arguments to the invoke member operation. 71 | /// 72 | /// 73 | /// The new representing the result of the binding. 74 | /// 75 | /// 76 | /// There is an attempt to dynamically access a class member that does not exist. 77 | /// 78 | public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) 79 | { 80 | var self = Expression; 81 | var exposed = (Exposed)Value!; 82 | 83 | var argTypes = new Type[args.Length]; 84 | var argExps = new Expression[args.Length]; 85 | 86 | for (int i = 0; i < args.Length; ++i) 87 | { 88 | argTypes[i] = args[i].LimitType; 89 | argExps[i] = args[i].Expression; 90 | 91 | if (args[i].Expression is ParameterExpression pe && pe.IsByRef) 92 | argTypes[i] = argTypes[i].MakeByRefType(); 93 | } 94 | 95 | var type = exposed.SubjectType; 96 | var declaringType = type; 97 | MethodInfo? method; 98 | do 99 | { 100 | method = declaringType.GetMethod(binder.Name, GetBindingFlags(), null, argTypes, null); 101 | } 102 | while (method == null && (declaringType = declaringType.BaseType) != null); 103 | 104 | if (method == null) 105 | { 106 | throw new MissingMemberException(type.FullName, binder.Name); 107 | } 108 | 109 | var @this = isStatic 110 | ? null 111 | : Expression.Convert(Expression.Field(Expression.Convert(self, typeof(Exposed)), "value"), method.ReflectedType ?? type); 112 | 113 | var target = Expression.Call(@this, method, argExps); 114 | var restrictions = BindingRestrictions.GetTypeRestriction(self, typeof(Exposed)); 115 | 116 | return new DynamicMetaObject(ConvertExpressionType(binder.ReturnType, target), restrictions); 117 | } 118 | 119 | /// 120 | /// Performs the binding of the dynamic get member operation. 121 | /// 122 | /// 123 | /// An instance of the that represents the details of the dynamic operation. 124 | /// 125 | /// 126 | /// The new representing the result of the binding. 127 | /// 128 | public override DynamicMetaObject BindGetMember(GetMemberBinder binder) 129 | { 130 | var self = Expression; 131 | 132 | var memberExpression = GetMemberExpression(self, binder.Name); 133 | 134 | var target = Expression.Convert(memberExpression, binder.ReturnType); 135 | var restrictions = BindingRestrictions.GetTypeRestriction(self, typeof(Exposed)); 136 | 137 | return new DynamicMetaObject(target, restrictions); 138 | } 139 | 140 | /// 141 | /// Performs the binding of the dynamic set member operation. 142 | /// 143 | /// 144 | /// An instance of the that represents the details of the dynamic operation. 145 | /// 146 | /// 147 | /// The representing the value for the set member operation. 148 | /// 149 | /// 150 | /// The new representing the result of the binding. 151 | /// 152 | public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) 153 | { 154 | var self = Expression; 155 | 156 | var memberExpression = GetMemberExpression(self, binder.Name); 157 | 158 | var target = 159 | Expression.Convert( 160 | Expression.Assign(memberExpression, Expression.Convert(value.Expression, memberExpression.Type)), 161 | binder.ReturnType); 162 | var restrictions = BindingRestrictions.GetTypeRestriction(self, typeof(Exposed)); 163 | 164 | return new DynamicMetaObject(target, restrictions); 165 | } 166 | 167 | /// 168 | /// Generates the for accessing a member by name. 169 | /// 170 | /// 171 | /// The for accessing the instance. 172 | /// 173 | /// 174 | /// The member name. 175 | /// 176 | /// 177 | /// for accessing a member by name. 178 | /// 179 | /// 180 | /// 181 | private MemberExpression GetMemberExpression(Expression self, string memberName) 182 | { 183 | MemberExpression? memberExpression = null; 184 | var type = ((Exposed)Value!).SubjectType; 185 | var declaringType = type; 186 | 187 | do 188 | { 189 | var property = declaringType.GetProperty(memberName, GetBindingFlags()); 190 | if (property != null) 191 | { 192 | memberExpression = Expression.Property(ConvertedExpression(property.ReflectedType ?? type), property); 193 | } 194 | else 195 | { 196 | var field = declaringType.GetField(memberName, GetBindingFlags()); 197 | if (field != null) 198 | { 199 | memberExpression = Expression.Field(ConvertedExpression(field.ReflectedType ?? type), field); 200 | } 201 | } 202 | } 203 | while (memberExpression == null && (declaringType = declaringType.BaseType) != null); 204 | 205 | if (memberExpression == null) 206 | { 207 | throw new MissingMemberException(type.FullName, memberName); 208 | } 209 | 210 | return memberExpression; 211 | 212 | Expression? ConvertedExpression(Type memberType) => isStatic 213 | ? null 214 | : Expression.Convert(Expression.Field(Expression.Convert(self, typeof(Exposed)), "value"), memberType); 215 | } 216 | 217 | /// 218 | /// Coerces the expression type into the expected return type. 219 | /// 220 | /// Type expeted at the dispatch site. 221 | /// Expression to coerce. 222 | /// Dynamic dispatch expects a method to return . 223 | /// Target expression coerced to the required type. 224 | private static Expression ConvertExpressionType(Type expectedType, Expression target) 225 | { 226 | if (target.Type == expectedType) 227 | { 228 | return target; 229 | } 230 | if (target.Type == typeof(void)) 231 | { 232 | return Expression.Block(target, Expression.Default(expectedType)); 233 | } 234 | if (expectedType == typeof(void)) 235 | { 236 | return Expression.Block(target, Expression.Empty()); 237 | } 238 | return Expression.Convert(target, expectedType); 239 | } 240 | 241 | /// 242 | /// Returns for member search. 243 | /// 244 | /// 245 | /// Static or instance flags depending on . 246 | /// 247 | private BindingFlags GetBindingFlags() 248 | { 249 | return isStatic 250 | ? BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic 251 | : BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly; 252 | } 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /ExposedObject/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | [assembly:CLSCompliant(false)] 4 | -------------------------------------------------------------------------------- /ExposedObject/PublicAPI.Shipped.txt: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | ExposedObject.Exposed 3 | override ExposedObject.Exposed.GetMetaObject(System.Linq.Expressions.Expression! parameter) -> System.Dynamic.DynamicMetaObject! 4 | static ExposedObject.Exposed.From(System.Type! type) -> dynamic! 5 | static ExposedObject.Exposed.From(object! subject) -> dynamic! 6 | static ExposedObject.Exposed.New(System.Type! type) -> dynamic! 7 | -------------------------------------------------------------------------------- /ExposedObject/PublicAPI.Unshipped.txt: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ExposedObject 2 | 3 | [![Build Status](https://travis-ci.org/skolima/ExposedObject.svg?branch=master)](https://travis-ci.org/skolima/ExposedObject) 4 | 5 | Fast dynamic wrapper for accessing hidden methods and fields of .Net objects. Uses 4.0 `dynamic` feature to allow seamless access to non-public object members. Facilitates white-box unit testing, exposes APIs that should be public and allows construction of elaborate hacks. Should not kill your cat provided you are careful. Available on [NuGet](http://nuget.org/packages/ExposedObject). 6 | 7 | ## Usage examples 8 | 9 | Handling private instance methods, fields and properties on a visible type: 10 | ```csharp 11 | // create an Exposed instance from a ClassWithHiddenMethods instance 12 | dynamic exposed = Exposed.From(new ClassWithHiddenMethods()); 13 | // calling a private method 14 | string password = exposed.GeneratePassword(8); 15 | // reading a private field 16 | int privateFieldValue = exposed.internalCount; 17 | // setting a private field 18 | exposed.internalCount = privateFieldValue * 2; 19 | // reading a protected property 20 | char protectedPropertyValue = exposed.InternalData; 21 | ``` 22 | 23 | Accessing private field on a hidden type: 24 | ```csharp 25 | // get the Type via reflection 26 | Type hiddenClass = Type.GetType("TestSubjects.HiddenClass, TestSubjects"); 27 | // call the parameterless constructor of a hidden type 28 | dynamic exposed = Exposed.New(hiddenClass); 29 | string password = exposed.password; 30 | ``` 31 | 32 | Accessing internal static method from type: 33 | ```csharp 34 | dynamic exposed = Exposed.From(typeof(ClassWithInternalStaticMethod)); 35 | decimal convertValue = exposed.ConvertValue(8); 36 | ``` 37 | 38 | ## TODO: 39 | 40 | - performance assessment (binding restrictions vs caching, compare with [CreateDelegate](http://msmvps.com/blogs/jon_skeet/archive/2008/08/09/making-reflection-fly-and-exploring-delegates.aspx) solution) 41 | - generic methods (there are tests for them, some suggested solutions on [StackOverflow](http://stackoverflow.com/questions/6954069/how-can-i-handle-generic-method-invocations-in-my-dynamicobject) ) 42 | - constructors (currently `Exposed.New()` delegates to `Activator.CreateInstance()` ) 43 | 44 | ## Ideas borrowed from: 45 | 46 | - [Bug squash: Testing private methods with C# 4.0](http://bugsquash.blogspot.com/2009/05/testing-private-methods-with-c-40.html) - starting idea 47 | - [Igor Ostrovsky: Use C# dynamic typing to conveniently access internals of an object](http://igoro.com/archive/use-c-dynamic-typing-to-conveniently-access-internals-of-an-object/) - convenient API 48 | - [Miguel de Icaza: C# 4's Dynamic in Mono](http://tirania.org/blog/archive/2009/Aug-11.html) - performant dynamic object implementation using [`GetMetaObject()`](http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject.getmetaobject) 49 | -------------------------------------------------------------------------------- /TestSubjects/CallSiteCachingClasses.cs: -------------------------------------------------------------------------------- 1 | namespace TestSubjects 2 | { 3 | public class BaseClass 4 | { 5 | #pragma warning disable CA1051 // Do not declare visible instance fields 6 | protected string _field = nameof(BaseClass); 7 | #pragma warning restore CA1051 // Do not declare visible instance fields 8 | 9 | protected string Property 10 | { 11 | get 12 | { 13 | return _field; 14 | } 15 | set 16 | { 17 | _field = value; 18 | } 19 | } 20 | protected string Method() => _field; 21 | 22 | 23 | public virtual string OverriddenMethod() => nameof(BaseClass); 24 | public virtual string OverriddenProperty => nameof(BaseClass); 25 | 26 | public string HiddenMethod() => nameof(BaseClass); 27 | 28 | public string HiddenProperty => nameof(BaseClass); 29 | 30 | #pragma warning disable CA1051 // Do not declare visible instance fields 31 | protected string _hiddenField = nameof(BaseClass); 32 | #pragma warning restore CA1051 // Do not declare visible instance fields 33 | } 34 | 35 | public class ChildClass : BaseClass 36 | { 37 | public ChildClass() 38 | { 39 | _field = nameof(ChildClass); 40 | } 41 | 42 | public override string OverriddenMethod() => nameof(ChildClass); 43 | public override string OverriddenProperty => nameof(ChildClass); 44 | 45 | public new string HiddenMethod() => nameof(ChildClass); 46 | 47 | public new string HiddenProperty => nameof(ChildClass); 48 | 49 | #pragma warning disable CA1051 // Do not declare visible instance fields 50 | protected new string _hiddenField = nameof(ChildClass); 51 | #pragma warning restore CA1051 // Do not declare visible instance fields 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /TestSubjects/ClassWithByRefParameters.cs: -------------------------------------------------------------------------------- 1 | // Author: 2 | // Manuel Josupeit-Walter (info@josupeit.com) 3 | // 4 | // (C) 2013 Cognifide 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining 7 | // a copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to 12 | // the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | using System.Globalization; 27 | 28 | #pragma warning disable CA1822 // because we want to test instance methods 29 | 30 | namespace TestSubjects 31 | { 32 | public class ClassWithByRefParameters 33 | { 34 | public int PublicMethod(int param1, out string param2, in long param3, ref byte param4) 35 | { 36 | param2 = param3.ToString(CultureInfo.InvariantCulture); 37 | param4 = unchecked((byte)param1); 38 | return 0; 39 | } 40 | 41 | protected int ProtectedMethod(int param1, out string param2, in long param3, ref byte param4) 42 | { 43 | param2 = param3.ToString(CultureInfo.InvariantCulture); 44 | param4 = unchecked((byte)param1); 45 | return 0; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /TestSubjects/ClassWithHiddenMethods.cs: -------------------------------------------------------------------------------- 1 | // Author: 2 | // Leszek Ciesielski (skolima@gmail.com) 3 | // Manuel Josupeit-Walter (info@josupeit.com) 4 | // 5 | // (C) 2013 Cognifide 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | using System; 28 | using System.Globalization; 29 | 30 | namespace TestSubjects 31 | { 32 | #pragma warning disable CA1708 // Identifiers should differ by more than case 33 | public class ClassWithHiddenMethods 34 | #pragma warning restore CA1708 // Identifiers should differ by more than case 35 | { 36 | private int _count; 37 | 38 | #pragma warning disable IDE0051 // Remove unused private members 39 | private int Count { get { return _count; } set { _count = value; } } 40 | #pragma warning restore IDE0051 // Remove unused private members 41 | 42 | #pragma warning disable CA1051 // Do not declare visible instance fields 43 | protected string password; 44 | #pragma warning restore CA1051 // Do not declare visible instance fields 45 | 46 | protected string Password 47 | { 48 | get 49 | { 50 | return password; 51 | } 52 | set 53 | { 54 | password = value; 55 | } 56 | } 57 | 58 | protected string GeneratePassword(int seed) 59 | { 60 | var random = new Random(seed); 61 | #pragma warning disable CA5394 // Do not use insecure randomness 62 | return password = random.Next().ToString(CultureInfo.InvariantCulture); 63 | #pragma warning restore CA5394 // Do not use insecure randomness 64 | } 65 | 66 | #pragma warning disable IDE0051 // Remove unused private members 67 | private void SetPassword(string newPassword) 68 | #pragma warning restore IDE0051 // Remove unused private members 69 | { 70 | password = newPassword; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /TestSubjects/ClassWithInheritedPrivateMembers.cs: -------------------------------------------------------------------------------- 1 | // Author: 2 | // Leszek Ciesielski (skolima@gmail.com) 3 | // Manuel Josupeit-Walter (info@josupeit.com) 4 | // 5 | // (C) 2013 Cognifide 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | namespace TestSubjects 28 | { 29 | public class ClassWithInheritedPrivateMembers : ClassWithHiddenMethods 30 | { 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /TestSubjects/GenericMethodClass.cs: -------------------------------------------------------------------------------- 1 | // Author: 2 | // Leszek Ciesielski (skolima@gmail.com) 3 | // 4 | // (C) 2011 Cognifide 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining 7 | // a copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to 12 | // the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | namespace TestSubjects 27 | { 28 | #pragma warning disable CA1812 // Class is never instantiated 29 | class GenericMethodClass 30 | #pragma warning restore CA1812 // Class is never instantiated 31 | { 32 | #pragma warning disable CA1822 // Mark members as static 33 | private string Mangle(T1 man, T2 gle) 34 | #pragma warning restore CA1822 // Mark members as static 35 | { 36 | return man.ToString() + gle; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /TestSubjects/HiddenClass.cs: -------------------------------------------------------------------------------- 1 | // Author: 2 | // Leszek Ciesielski (skolima@gmail.com) 3 | // 4 | // (C) 2011 Cognifide 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining 7 | // a copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to 12 | // the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | namespace TestSubjects 27 | { 28 | #pragma warning disable CA1812 // Class is never instantiated 29 | class HiddenClass : ClassWithHiddenMethods 30 | #pragma warning restore CA1812 // Class is never instantiated 31 | { 32 | private int Countz { get; set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /TestSubjects/OverloadedMethodsClass.cs: -------------------------------------------------------------------------------- 1 | // Author: 2 | // Leszek Ciesielski (skolima@gmail.com) 3 | // 4 | // (C) 2011 Cognifide 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining 7 | // a copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to 12 | // the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | namespace TestSubjects 27 | { 28 | #pragma warning disable CA1812 // Class is never instantiated 29 | class OverloadedMethodsClass 30 | #pragma warning restore CA1812 // Class is never instantiated 31 | { 32 | #pragma warning disable CA1822 // Mark members as static 33 | public string SuperMethod() 34 | #pragma warning restore CA1822 // Mark members as static 35 | { 36 | return "SuperMethod"; 37 | } 38 | 39 | #pragma warning disable CA1822 // Mark members as static 40 | #pragma warning disable CA1801 // Review unused parameters 41 | protected string SuperMethod(int var) 42 | #pragma warning restore CA1801 // Review unused parameters 43 | #pragma warning restore CA1822 // Mark members as static 44 | { 45 | return "SuperMethod_int"; 46 | } 47 | 48 | #pragma warning disable CA1822 // Mark members as static 49 | #pragma warning disable CA1801 // Review unused parameters 50 | private string SuperMethod(string value) 51 | #pragma warning restore CA1801 // Review unused parameters 52 | #pragma warning restore CA1822 // Mark members as static 53 | { 54 | return "SuperMethod_string"; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /TestSubjects/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | [assembly:CLSCompliant(false)] 4 | -------------------------------------------------------------------------------- /TestSubjects/StaticClass.cs: -------------------------------------------------------------------------------- 1 | // Author: 2 | // Leszek Ciesielski (skolima@gmail.com) 3 | // 4 | // (C) 2011 Cognifide 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining 7 | // a copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to 12 | // the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | namespace TestSubjects 27 | { 28 | class StaticClass 29 | { 30 | #pragma warning disable 414 31 | #pragma warning disable CA1823 // Avoid unused private fields 32 | private static string testValue = "testValue"; 33 | #pragma warning restore CA1823 // Avoid unused private fields 34 | #pragma warning restore 414 35 | 36 | internal static decimal ConvertValue(int valueToConvert) 37 | { 38 | return Value = valueToConvert * 1.2m; 39 | } 40 | 41 | protected static decimal Value { get; set; } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /TestSubjects/TestSubjects.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net462;net6.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /Tests/CallSiteCachingTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using ExposedObject; 7 | using TestSubjects; 8 | using Xunit; 9 | 10 | namespace Tests 11 | { 12 | public class CallSiteCachingTest 13 | { 14 | [Fact] 15 | public void OverriddenMethodTest() 16 | { 17 | dynamic child = Exposed.From(new ChildClass()); 18 | Assert.Equal(nameof(ChildClass), child.OverriddenMethod()); 19 | } 20 | 21 | [Fact] 22 | public void OverriddenPropertyTest() 23 | { 24 | dynamic child = Exposed.From(new ChildClass()); 25 | Assert.Equal(nameof(ChildClass), child.OverriddenProperty); 26 | } 27 | 28 | [Fact] 29 | public void HiddenMethodTest() 30 | { 31 | dynamic child = Exposed.From(new ChildClass()); 32 | Assert.Equal(nameof(ChildClass), child.HiddenMethod()); 33 | } 34 | 35 | [Fact] 36 | public void HiddenPropertyTest() 37 | { 38 | dynamic child = Exposed.From(new ChildClass()); 39 | Assert.Equal(nameof(ChildClass), child.HiddenProperty); 40 | } 41 | 42 | [Fact] 43 | public void HiddenFieldTest() 44 | { 45 | dynamic child = Exposed.From(new ChildClass()); 46 | Assert.Equal(nameof(ChildClass), child._hiddenField); 47 | } 48 | 49 | [Fact] 50 | public void CallSiteTypeCachingInheritedMethodTest() 51 | { 52 | CallHiddenMethod(new ChildClass()); 53 | CallHiddenMethod(new BaseClass()); 54 | 55 | static string CallHiddenMethod(Object o) 56 | { 57 | dynamic exposed = Exposed.From(o); 58 | return exposed.Method(); 59 | } 60 | } 61 | 62 | [Fact] 63 | public void CallSiteTypeCachingInheritedPropertyTest() 64 | { 65 | var childValue = GetHiddenPropertyValue(new ChildClass()); 66 | var parentValue = GetHiddenPropertyValue(new BaseClass()); 67 | 68 | Assert.Equal(nameof(ChildClass), childValue); 69 | Assert.Equal(nameof(BaseClass), parentValue); 70 | 71 | static string GetHiddenPropertyValue(Object o) 72 | { 73 | dynamic exposed = Exposed.From(o); 74 | return exposed.Property; 75 | } 76 | } 77 | 78 | [Fact] 79 | public void CallSiteTypeCachingInheritedFieldTest() 80 | { 81 | var childValue = GetHiddenFieldValue(new ChildClass()); 82 | var parentValue = GetHiddenFieldValue(new BaseClass()); 83 | 84 | Assert.Equal(nameof(ChildClass), childValue); 85 | Assert.Equal(nameof(BaseClass), parentValue); 86 | 87 | static string GetHiddenFieldValue(Object o) 88 | { 89 | dynamic exposed = Exposed.From(o); 90 | return exposed._field; 91 | } 92 | } 93 | 94 | // FIXME: The below show advanced situations where the call site type caching isn't as easily worked around 95 | [Fact(Skip = "Not yet working")] 96 | public void CallSiteTypeCachingHiddenMethodTest() 97 | { 98 | Assert.Equal(nameof(ChildClass), GetHiddenMethodValue(new ChildClass())); 99 | Assert.Equal(nameof(BaseClass), GetHiddenMethodValue(new BaseClass())); 100 | 101 | static string GetHiddenMethodValue(Object o) 102 | { 103 | dynamic exposed = Exposed.From(o); 104 | return exposed.HiddenMethod(); 105 | } 106 | } 107 | 108 | [Fact(Skip = "Not yet working")] 109 | public void CallSiteTypeCachingHiddenPropertyTest() 110 | { 111 | Assert.Equal(nameof(ChildClass), GetHiddenPropertyValue(new ChildClass())); 112 | Assert.Equal(nameof(BaseClass), GetHiddenPropertyValue(new BaseClass())); 113 | 114 | static string GetHiddenPropertyValue(Object o) 115 | { 116 | dynamic exposed = Exposed.From(o); 117 | return exposed.HiddenProperty; 118 | } 119 | } 120 | 121 | [Fact(Skip = "Not yet working")] 122 | public void CallSiteTypeCachingHiddenFieldTest() 123 | { 124 | Assert.Equal(nameof(ChildClass), GetHiddenFieldValue(new ChildClass())); 125 | 126 | // If this test fails, will throw an InvalidCastException 127 | Assert.Equal(nameof(BaseClass), GetHiddenFieldValue(new BaseClass())); 128 | 129 | static string GetHiddenFieldValue(Object o) 130 | { 131 | dynamic exposed = Exposed.From(o); 132 | return exposed._hiddenField; 133 | } 134 | } 135 | 136 | [Fact(Skip = "Not yet working")] 137 | public void CallSiteTypeCachingOverriddenMethodTest() 138 | { 139 | Assert.Equal(nameof(ChildClass), GetOverriddenMethodValue(new ChildClass())); 140 | 141 | // If this test fails, will throw an InvalidCastException 142 | Assert.Equal(nameof(BaseClass), GetOverriddenMethodValue(new BaseClass())); 143 | 144 | static string GetOverriddenMethodValue(Object o) 145 | { 146 | dynamic exposed = Exposed.From(o); 147 | return exposed.OverriddenMethod(); 148 | } 149 | } 150 | 151 | [Fact(Skip = "Not yet working")] 152 | public void CallSiteTypeCachingOverriddenPropertyTest() 153 | { 154 | Assert.Equal(nameof(ChildClass), GetOverriddenPropertyValue(new ChildClass())); 155 | 156 | // If this test fails, will throw an InvalidCastException 157 | Assert.Equal(nameof(BaseClass), GetOverriddenPropertyValue(new BaseClass())); 158 | 159 | static string GetOverriddenPropertyValue(Object o) 160 | { 161 | dynamic exposed = Exposed.From(o); 162 | return exposed.OverriddenProperty; 163 | } 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /Tests/ClassWithByRefParametersTest.cs: -------------------------------------------------------------------------------- 1 | // Author: 2 | // Manuel Josupeit-Walter (info@josupeit.com) 3 | // 4 | // (C) 2011 Cognifide 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining 7 | // a copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to 12 | // the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | using ExposedObject; 27 | using TestSubjects; 28 | using Xunit; 29 | 30 | namespace Tests 31 | { 32 | public class ClassWithByRefParametersTest 33 | { 34 | [Fact] 35 | public void PublicMethodTest() 36 | { 37 | var exposed = Exposed.From(new ClassWithByRefParameters()); 38 | 39 | var param1 = 123; 40 | string param2 = null; 41 | var param3 = 2937842L; 42 | byte param4 = 111; 43 | 44 | int result = exposed.PublicMethod(param1, ref param2, ref param3, ref param4); 45 | 46 | Assert.Equal(0, result); 47 | Assert.Equal("2937842", param2); 48 | Assert.Equal(123, param4); 49 | } 50 | 51 | [Fact] 52 | public void ProtectedMethodTest() 53 | { 54 | var exposed = Exposed.From(new ClassWithByRefParameters()); 55 | 56 | var param1 = 123; 57 | string param2 = null; 58 | var param3 = 2937842L; 59 | byte param4 = 111; 60 | 61 | int result = exposed.ProtectedMethod(param1, ref param2, ref param3, ref param4); 62 | 63 | Assert.Equal(0, result); 64 | Assert.Equal("2937842", param2); 65 | Assert.Equal(123, param4); 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /Tests/ClassWithHiddenMethodsTest.cs: -------------------------------------------------------------------------------- 1 | // Author: 2 | // Leszek Ciesielski (skolima@gmail.com) 3 | // 4 | // (C) 2011 Cognifide 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining 7 | // a copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to 12 | // the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | using System.Collections.Concurrent; 27 | using System.Collections.Generic; 28 | using System.Threading; 29 | 30 | using ExposedObject; 31 | 32 | using TestSubjects; 33 | 34 | using Xunit; 35 | 36 | namespace Tests 37 | { 38 | public class ClassWithHiddenMethodsTest 39 | { 40 | [Fact] 41 | public void FieldTest() 42 | { 43 | dynamic exposed = Exposed.From(new ClassWithHiddenMethods()); 44 | string password = exposed.password; 45 | Assert.Null(password); 46 | 47 | exposed.password = "TestValue"; 48 | password = exposed.password; 49 | Assert.Equal("TestValue", password); 50 | } 51 | 52 | [Fact] 53 | public void MethodTest() 54 | { 55 | dynamic exposed = Exposed.New(typeof(ClassWithHiddenMethods)); 56 | string password = exposed.GeneratePassword(8); 57 | 58 | Assert.Equal(password, exposed.Password); 59 | } 60 | 61 | [Fact] 62 | public void PropertyTest() 63 | { 64 | dynamic exposed = Exposed.From(new ClassWithHiddenMethods()); 65 | int count = exposed.Count; 66 | Assert.Equal(0, count); 67 | 68 | exposed.Count = 9; 69 | count = exposed.Count; 70 | Assert.Equal(9, count); 71 | } 72 | 73 | [Fact] 74 | public void ReturnVoidMethodTest() 75 | { 76 | dynamic exposed = Exposed.From(new ClassWithHiddenMethods()); 77 | exposed.SetPassword("new test password"); 78 | 79 | Assert.Equal("new test password", exposed.password); 80 | } 81 | 82 | [Fact] 83 | public void ThreadingSafetySimpleTest() 84 | { 85 | var results = new ConcurrentDictionary(); 86 | const int threadsCount = 100; 87 | var threads = new List(threadsCount); 88 | for (int i = 0; i < threadsCount; i++) 89 | { 90 | var thread = new Thread( 91 | o => 92 | { 93 | var password = o.ToString(); 94 | var number = (int)o; 95 | var exposed = Exposed.From(new ClassWithHiddenMethods()); 96 | Thread.Sleep(threadsCount - number); 97 | exposed.SetPassword(password); 98 | Thread.Sleep(threadsCount - number); 99 | string read = exposed.Password; 100 | results[number] = password == read; 101 | }); 102 | threads.Add(thread); 103 | thread.Start(i); 104 | } 105 | for (int i = 0; i < threadsCount; i++) 106 | { 107 | threads[i].Join(); 108 | Assert.True(results[i]); 109 | } 110 | Assert.Equal(threadsCount, results.Count); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Tests/ClassWithInheritedPrivateMembersTest.cs: -------------------------------------------------------------------------------- 1 | // Author: 2 | // Leszek Ciesielski (skolima@gmail.com) 3 | // Manuel Josupeit-Walter (info@josupeit.com) 4 | // 5 | // (C) 2013 Cognifide 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | using ExposedObject; 28 | using TestSubjects; 29 | using Xunit; 30 | 31 | namespace Tests 32 | { 33 | public class ClassWithInheritedPrivateMembersTest 34 | { 35 | [Fact] 36 | public void PrivatePropertyTest() 37 | { 38 | dynamic exposed = Exposed.From(new ClassWithInheritedPrivateMembers()); 39 | int count = exposed.Count; 40 | Assert.Equal(0, count); 41 | exposed.Count = 8; 42 | count = exposed.Count; 43 | Assert.Equal(8, count); 44 | } 45 | 46 | [Fact] 47 | public void PrivateMethodTest() 48 | { 49 | dynamic exposed = Exposed.From(new ClassWithInheritedPrivateMembers()); 50 | exposed.SetPassword("test pass"); 51 | string password = exposed.Password; 52 | Assert.Equal("test pass", password); 53 | } 54 | 55 | [Fact] 56 | public void PrivateFieldTest() 57 | { 58 | dynamic exposed = Exposed.From(new ClassWithInheritedPrivateMembers()); 59 | int count = exposed._count; 60 | Assert.Equal(0, count); 61 | exposed._count = 8; 62 | count = exposed.Count; 63 | Assert.Equal(8, count); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Tests/GenericMethodClassTest.cs: -------------------------------------------------------------------------------- 1 | // Author: 2 | // Leszek Ciesielski (skolima@gmail.com) 3 | // 4 | // (C) 2011 Cognifide 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining 7 | // a copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to 12 | // the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | using System; 27 | 28 | using ExposedObject; 29 | 30 | using Xunit; 31 | 32 | namespace Tests 33 | { 34 | public class GenericMethodClassTest 35 | { 36 | [Fact(Skip = "Not working")] 37 | public void MethodTest() 38 | { 39 | dynamic exposed = Exposed.New(Type.GetType("TestSubjects.GenericMethodClass, TestSubjects")); 40 | string password = exposed.Mangle("test", 8); 41 | 42 | Assert.Equal("test8", password); 43 | } 44 | 45 | [Fact(Skip = "Not working")] 46 | public void MismatchedMethodTest() 47 | { 48 | dynamic exposed = Exposed.New(Type.GetType("TestSubjects.GenericMethodClass, TestSubjects")); 49 | 50 | Assert.Throws(() => exposed.Mangle("test", 8)); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Tests/HiddenClassTest.cs: -------------------------------------------------------------------------------- 1 | // Author: 2 | // Leszek Ciesielski (skolima@gmail.com) 3 | // Manuel Josupeit-Walter (info@josupeit.com) 4 | // 5 | // (C) 2013 Cognifide 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | using System; 28 | 29 | using ExposedObject; 30 | 31 | using Xunit; 32 | 33 | namespace Tests 34 | { 35 | public class HiddenClassTest 36 | { 37 | [Fact] 38 | public void FieldTest() 39 | { 40 | dynamic exposed = Exposed.New(Type.GetType("TestSubjects.HiddenClass, TestSubjects")); 41 | string password = exposed.password; 42 | Assert.Null(password); 43 | 44 | exposed.password = "TestValue"; 45 | password = exposed.password; 46 | Assert.Equal("TestValue", password); 47 | } 48 | 49 | [Fact] 50 | public void MethodTest() 51 | { 52 | dynamic exposed = Exposed.New(Type.GetType("TestSubjects.HiddenClass, TestSubjects")); 53 | string password = exposed.GeneratePassword(8); 54 | 55 | Assert.Equal(password, exposed.Password); 56 | } 57 | 58 | [Fact] 59 | public void PropertyTest() 60 | { 61 | dynamic exposed = Exposed.New(Type.GetType("TestSubjects.HiddenClass, TestSubjects")); 62 | int count = exposed.Countz; 63 | Assert.Equal(0, count); 64 | 65 | exposed.Countz = 9; 66 | count = exposed.Countz; 67 | Assert.Equal(9, count); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Tests/OverloadedMethodsClassTest.cs: -------------------------------------------------------------------------------- 1 | // Author: 2 | // Leszek Ciesielski (skolima@gmail.com) 3 | // 4 | // (C) 2011 Cognifide 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining 7 | // a copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to 12 | // the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | using System; 27 | 28 | using ExposedObject; 29 | 30 | using Xunit; 31 | 32 | namespace Tests 33 | { 34 | public class OverloadedMethodsClassTest 35 | { 36 | [Fact] 37 | public void OverloadResolutionTest() 38 | { 39 | dynamic exposed = Exposed.New(Type.GetType("TestSubjects.OverloadedMethodsClass, TestSubjects")); 40 | 41 | string password = exposed.SuperMethod(); 42 | Assert.Equal("SuperMethod", password); 43 | 44 | password = exposed.SuperMethod(3); 45 | Assert.Equal("SuperMethod_int", password); 46 | 47 | password = exposed.SuperMethod("string"); 48 | Assert.Equal("SuperMethod_string", password); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | [assembly:CLSCompliant(false)] 4 | -------------------------------------------------------------------------------- /Tests/StaticClassTest.cs: -------------------------------------------------------------------------------- 1 | // Author: 2 | // Leszek Ciesielski (skolima@gmail.com) 3 | // 4 | // (C) 2011 Cognifide 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining 7 | // a copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to 12 | // the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | using System; 27 | 28 | using ExposedObject; 29 | 30 | using Xunit; 31 | 32 | namespace Tests 33 | { 34 | public class StaticClassTest 35 | { 36 | [Fact] 37 | public void FieldTest() 38 | { 39 | dynamic exposed = Exposed.From(Type.GetType("TestSubjects.StaticClass, TestSubjects")); 40 | string testValue = exposed.testValue; 41 | Assert.Equal("testValue", testValue); 42 | 43 | exposed.testValue = "TestValue"; 44 | testValue = exposed.testValue; 45 | Assert.Equal("TestValue", testValue); 46 | } 47 | 48 | [Fact] 49 | public void MethodTest() 50 | { 51 | dynamic exposed = Exposed.From(Type.GetType("TestSubjects.StaticClass, TestSubjects")); 52 | decimal convertValue = exposed.ConvertValue(8); 53 | 54 | Assert.Equal(convertValue, exposed.Value); 55 | } 56 | 57 | [Fact] 58 | public void PropertyTest() 59 | { 60 | dynamic exposed = Exposed.From(Type.GetType("TestSubjects.StaticClass, TestSubjects")); 61 | exposed.Value = 9; 62 | decimal count = exposed.Value; 63 | Assert.Equal(9, count); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Tests/Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net462;net6.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | --------------------------------------------------------------------------------