diff --git a/ExposedObject/Exposed.cs b/ExposedObject/Exposed.cs
index 6332b67..cc077ea 100644
--- a/ExposedObject/Exposed.cs
+++ b/ExposedObject/Exposed.cs
@@ -79,7 +79,12 @@ private Exposed(Type type)
///
/// Gets the of the exposed object.
///
- internal Type SubjectType { get; private set; }
+ public Type SubjectType { get; private set; }
+
+ ///
+ /// Gets the wrapped value.
+ ///
+ public object? Value => value;
///
/// Creates a new wrapper for accessing members of subject.
diff --git a/ExposedObject/MetaObject.cs b/ExposedObject/MetaObject.cs
index 209c2e8..c46f4cb 100644
--- a/ExposedObject/MetaObject.cs
+++ b/ExposedObject/MetaObject.cs
@@ -42,6 +42,9 @@ internal sealed class MetaObject : DynamicMetaObject
///
private readonly bool isStatic;
+ private Exposed ExposedInstance => (Exposed)Value!;
+ private Type SubjectType => ExposedInstance.SubjectType;
+
///
/// Initializes a new instance of the class.
///
@@ -54,7 +57,7 @@ internal sealed class MetaObject : DynamicMetaObject
///
/// Should this MetaObject bind to or instance methods and fields.
///
- public MetaObject(Expression expression, object value, bool staticBind) :
+ public MetaObject(Expression expression, Exposed value, bool staticBind) :
base(expression, BindingRestrictions.Empty, value)
{
isStatic = staticBind;
@@ -78,7 +81,7 @@ public MetaObject(Expression expression, object value, bool staticBind) :
public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
{
var self = Expression;
- var exposed = (Exposed)Value;
+ var exposed = ExposedInstance;
var argTypes = new Type[args.Length];
var argExps = new Expression[args.Length];
@@ -92,7 +95,7 @@ public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, Dy
argTypes[i] = argTypes[i].MakeByRefType();
}
- var type = exposed.SubjectType;
+ var type = SubjectType;
var declaringType = type;
#if EXPOSED_NULLABLE
MethodInfo? method;
@@ -110,16 +113,50 @@ public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, Dy
throw new MissingMemberException(type.FullName, binder.Name);
}
- var @this = isStatic
- ? null
- : Expression.Convert(Expression.Field(Expression.Convert(self, typeof(Exposed)), "value"), type);
+ var thisRaw = GetThisExpressionRaw(self);
+ var @this = ConvertThis(thisRaw, type);
var target = Expression.Call(@this, method, argExps);
var restrictions = BindingRestrictions.GetTypeRestriction(self, typeof(Exposed));
+ restrictions = AppendRestrictions(thisRaw, restrictions, self);
return new DynamicMetaObject(ConvertExpressionType(binder.ReturnType, target), restrictions);
}
+ private BindingRestrictions AppendRestrictions(Expression? thisRaw, BindingRestrictions restrictions, Expression self)
+ {
+ if (thisRaw is { } thisExp)
+ {
+ restrictions = restrictions.Merge(BindingRestrictions.GetTypeRestriction(thisExp, SubjectType));
+ }
+ else
+ {
+ // Need to restrict the instance :(
+ restrictions = restrictions.Merge(BindingRestrictions.GetInstanceRestriction(self, ExposedInstance));
+ }
+
+ return restrictions;
+ }
+
+ private static Expression? ConvertThis(Expression? rawThis, Type type)
+ {
+ if (rawThis != null)
+ {
+ return Expression.Convert(rawThis, type);
+ }
+
+ return null;
+ }
+
+ private Expression? GetThisExpressionRaw(Expression self)
+ {
+ return isStatic
+ ? null
+ : Expression.Property(Expression.Convert(self, typeof(Exposed)), nameof(Exposed.Value));
+ }
+
+
+
///
/// Performs the binding of the dynamic get member operation.
///
@@ -133,14 +170,19 @@ public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
{
var self = Expression;
- var memberExpression = GetMemberExpression(self, binder.Name);
+ var type = SubjectType;
+ var thisRaw = GetThisExpressionRaw(self);
+ var @this = ConvertThis(thisRaw, type);
+ var memberExpression = GetMemberExpression(@this, binder.Name);
var target = Expression.Convert(memberExpression, binder.ReturnType);
var restrictions = BindingRestrictions.GetTypeRestriction(self, typeof(Exposed));
+ restrictions = AppendRestrictions(thisRaw, restrictions, self);
return new DynamicMetaObject(target, restrictions);
}
+
///
/// Performs the binding of the dynamic set member operation.
///
@@ -157,13 +199,17 @@ public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicM
{
var self = Expression;
- var memberExpression = GetMemberExpression(self, binder.Name);
+ var type = SubjectType;
+ var thisRaw = GetThisExpressionRaw(self);
+ var @this = ConvertThis(thisRaw, type);
+ var memberExpression = GetMemberExpression(@this, binder.Name);
var target =
Expression.Convert(
Expression.Assign(memberExpression, Expression.Convert(value.Expression, memberExpression.Type)),
binder.ReturnType);
var restrictions = BindingRestrictions.GetTypeRestriction(self, typeof(Exposed));
+ restrictions = AppendRestrictions(thisRaw, restrictions, self);
return new DynamicMetaObject(target, restrictions);
}
@@ -171,7 +217,7 @@ public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicM
///
/// Generates the for accessing a member by name.
///
- ///
+ ///
/// The for accessing the instance.
///
///
@@ -182,17 +228,14 @@ public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicM
///
///
///
- private MemberExpression GetMemberExpression(Expression self, string memberName)
+ private MemberExpression GetMemberExpression(Expression? @this, string memberName)
{
#if EXPOSED_NULLABLE
MemberExpression? memberExpression = null;
#else
MemberExpression memberExpression = null;
#endif
- var type = ((Exposed)Value).SubjectType;
- var @this = isStatic
- ? null
- : Expression.Convert(Expression.Field(Expression.Convert(self, typeof(Exposed)), "value"), type);
+ var type = SubjectType;
var declaringType = type;
do
diff --git a/TestSubjects/SimilarClasses.cs b/TestSubjects/SimilarClasses.cs
new file mode 100644
index 0000000..eed3947
--- /dev/null
+++ b/TestSubjects/SimilarClasses.cs
@@ -0,0 +1,37 @@
+using System.Diagnostics.CodeAnalysis;
+// ReSharper disable UnusedType.Global
+// ReSharper disable UnusedMember.Global
+// ReSharper disable UnusedMember.Local
+// ReSharper disable UnusedParameter.Global
+// ReSharper disable UnusedParameter.Local
+
+namespace TestSubjects;
+
+[SuppressMessage("Performance", "CA1822:Mark members as static")]
+public class SimilarClass1
+{
+ public static string SimilarMethod(int arg)
+ {
+ return nameof(SimilarClass1);
+ }
+
+ private string InstanceMethod(int arg)
+ {
+ return nameof(SimilarClass1);
+ }
+}
+
+
+[SuppressMessage("Performance", "CA1822:Mark members as static")]
+public class SimilarClass2
+{
+ public static string SimilarMethod(int arg)
+ {
+ return nameof(SimilarClass2);
+ }
+
+ private string InstanceMethod(int arg)
+ {
+ return nameof(SimilarClass2);
+ }
+}
\ No newline at end of file
diff --git a/Tests/SimilarClassesTest.cs b/Tests/SimilarClassesTest.cs
new file mode 100644
index 0000000..30a47ec
--- /dev/null
+++ b/Tests/SimilarClassesTest.cs
@@ -0,0 +1,39 @@
+using System;
+using TestSubjects;
+using Xunit;
+
+namespace Tests;
+
+
+public class SimilarClassTest
+{
+ [Fact]
+ public void TestSimilarClassesInstances()
+ {
+ dynamic exposed1 = Exposed.New(Type.GetType("TestSubjects.SimilarClass1, TestSubjects"));
+ string result1 = exposed1.InstanceMethod(1);
+
+ dynamic exposed2 = Exposed.New(Type.GetType("TestSubjects.SimilarClass2, TestSubjects"));
+ string result2 = exposed2.InstanceMethod(2);
+
+ // No failure
+
+ Assert.Equal(nameof(SimilarClass1), result1);
+ Assert.Equal(nameof(SimilarClass2), result2);
+ }
+
+ [Fact]
+ public void TestSimilarClassesStatic()
+ {
+ dynamic exposed1 = Exposed.From(Type.GetType("TestSubjects.SimilarClass1, ExposedObject.TestSubjects"));
+ string result1 = exposed1.SimilarMethod(1);
+
+ dynamic exposed2 = Exposed.From(Type.GetType("TestSubjects.SimilarClass2, TestSubjects"));
+ string result2 = exposed2.SimilarMethod(2);
+
+ // No failure
+
+ Assert.Equal(nameof(SimilarClass1), result1);
+ Assert.Equal(nameof(SimilarClass2), result2);
+ }
+}
\ No newline at end of file