├── .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 | [](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 |
--------------------------------------------------------------------------------