An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
In short, the problem is solved if Test<A, B> behaves as Test<A, object>. .NET Framework has the RealProxy class. It is mainly used in AOP. With this, you can create a transparent proxy and treat Test<A, B> as Test<A, object>.
First, in order to create a transparent proxy, the Test<A, B> class must be a class that inherits MarshalByRefObject. Then, declare that implicit type conversion is possible. The content is transparent proxy creation processing.
abstract class Test<A, B> : MarshalByRefObject
{
public A ValueA { get; set; }
public B ValueB { get; set; }
private Test<B, object> t1;
protected Test(Test<B, object> t) {
t1 = t;
ValueA = default(A);
if (t != null) {
ValueB = t.ValueA;
}
}
public static implicit operator
Test<A, object>(Test<A, B> value) {
return UTL.CreateProxy(value);
}
}
Next, inherit the RealProxy class and create a class to create a transparent proxy. Convert Test<A, object> method to Test<A, B> method with Invoke method and execute.
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using System.Diagnostics;
static class UTL
{
public static Test<A, object> CreateProxy<A, B>(Test<A, B> test) {
var proxy = new TestProxy<A, B>(test);
return (Test<A, object>)proxy.GetTransparentProxy();
}
internal abstract class TestProxy : RealProxy
{
protected Type proxyType;
protected TestProxy(Type classToProxy)
: base(classToProxy) {
proxyType = classToProxy;
}
public abstract object Instanse { get; }
}
internal class TestProxy<A, B> : TestProxy
{
public override object Instanse => instance;
private readonly Test<A, B> instance;
private readonly Type instanceType;
private readonly Dictionary<MethodInfo, MethodInfo> mappings
= new Dictionary<MethodInfo, MethodInfo>();
const BindingFlags bindingFlags
= BindingFlags.Public | BindingFlags.Instance;
public TestProxy(Test<A, B> instance)
: base(typeof(Test<A, object>)) {
this.instance = instance;
instanceType = instance.GetType();
// Should be same.
MethodInfo[] proxyMethods = proxyType.GetMethods(bindingFlags);
MethodInfo[] instanceMethods = instanceType.GetMethods(bindingFlags);
for (int i = 0; i < instanceMethods.Length; i++) {
if (proxyMethods[i].Name != instanceMethods[i].Name) {
throw new InvalidProgramException();
}
mappings.Add(proxyMethods[i], instanceMethods[i]);
}
}
public override IMessage Invoke(IMessage msg) {
try {
IMethodMessage mm = msg as IMethodMessage;
object[] args = mm.Args;
MethodInfo proxyMethod = (MethodInfo)mm.MethodBase;
object ret = InvokeToInstance(proxyMethod, args);
return new ReturnMessage(ret, args, args.Length,
mm.LogicalCallContext, (IMethodCallMessage)msg);
} catch (Exception ex) {
if (ex.InnerException != null) {
return new ReturnMessage(ex.InnerException, (IMethodCallMessage)msg);
}
return new ReturnMessage(ex, (IMethodCallMessage)msg);
}
}
private object InvokeToInstance(MethodInfo proxyMethod, object[] args) {
if (mappings.TryGetValue(proxyMethod, out MethodInfo instanceMethod)) {
return instanceMethod.Invoke(instance, args);
}
// Field Get/Set
string fieldName = (string)args[1];
FieldInfo fi = instanceType.GetField(fieldName);
switch (proxyMethod.Name) {
case "FieldSetter":
fi.SetValue(instance, args[2]);
break;
case "FieldGetter":
args[2] = fi.GetValue(instance);
break;
default:
throw new InvalidProgramException("InvokeToInstance");
}
return null;
}
}
}
Unfortunately, it was deleted in .NET, and DispatchProxy class added, but since it is interface-based, it is not as easy to use as RealProxy, which is a real shame.
Let's try using it. You should see that Test<A, B> behaves like Test<A, object>.
static void Main(string[] args) {
Child3 c3 = new Child3(null);
c3.ValueA = true;
c3.ValueB = 1;
Test<bool, object> proxy = c3;
bool valueA = proxy.ValueA;
object valueB = proxy.ValueB;
Child2 c2 = new Child2(c3);
Child1 c1 = new Child1(c2);
}