Ayende @ Rahien

Refunds available at head office

Can you hack this out? Revised

It appears that I made this challenge a bit too hard, I wrote it in haste and didn’t check that it all works. Here is the new code, it is almost the same as the first, but now it doesn’t check the typeof(T) (for which there is no interception path), but the provided runnerType instead.

internal interface IRunner
{
void Execute();
}

public sealed class AssemblyRunner : IRunner
{
void IRunner.Execute()
{
Console.WriteLine("executing");
}
}

public class CompositeRunner
{
private readonly Type runnerType;

public CompositeRunner(Type runnerType)
{
this.runnerType = runnerType;
}

public void Execute()
{
if (!typeof(IRunner).IsAssignableFrom(runnerType))
throw new InvalidOperationException("invalid type");
var runner = (IRunner)Activator.CreateInstance(runnerType);
Console.WriteLine("starting");
runner.Execute();
Console.WriteLine("done");
}
}

Sorry about that image

Look at the future posts for hints for that.

Comments

Omer Mor
11/23/2009 08:20 PM by
Omer Mor

Now THAT'S easy!

I have a great solution for this, but it doesn't fit in this comment. :-)

Alex Simkin
11/23/2009 09:32 PM by
Alex Simkin

OK. You already forced me to look at ProfilingSqlProviderProxyType, so I cannot participate.

Pierre Fermat
11/23/2009 11:50 PM by
Pierre Fermat

Omar,

Been There, Done That!

Pierre

Stefan Wenig
11/24/2009 08:32 AM by
Stefan Wenig

now it seems almost simple. you can create a RealProxy for IRunner

class MyProxy : RealProxy {

MyProxy() : base (Type.GetType ("IRunner...")

and put a custom ProxyAttribute on the type you pass (must be derived from ContextBoundObject). Some fumbling with Invoke(), that should basically be it...

Ayende Rahien
11/24/2009 09:35 AM by
Ayende Rahien

Stefan,

Write the code, there is still one trip wire there.

Fabian Schmied
11/24/2009 10:02 AM by
Fabian Schmied

The one trip still there is the " if (!typeof(IRunner).IsAssignableFrom(runnerType))" check.

And this can be solved by:

  • subclassing System.Type (in actual C# code, no hackery required) and

  • overriding UnderlyingType

    • to return typeof (IRunner) on the first call and

    • typeof (MyContextBoundObject) on the second call.

Strangely, this works, it really does. At the moment, at least. Aren't you afraid to actually sell a piece of software based on this hack? :)

Stefan Wenig
11/24/2009 10:21 AM by
Stefan Wenig
Oh right, I was so p***ed about the cast in CreateInstance
<t that I forgot about that one. Couldn't find a better solution than Fabian either, everything else seems to end up in something internal/sealed. (I even had to look at the SSCLR, because reflector screws up in Type.IsAssignableFrom... enough unnatural acts for this month!)
  
So is this it, or is there a better way?
>
Ayende Rahien
11/24/2009 11:16 AM by
Ayende Rahien

Fabian,

I am really scared by that, yes.

OTOH, what other choice do I have?

Stefan Wenig
11/24/2009 11:30 AM by
Stefan Wenig

CreateInstance(Of T) that is. No angle brackets, must be a VB lover's blog ;-)

BTW, just when I realized that R# can't go all the way when subclassing Type via delegation to a member, I discoverd System.Reflection.TypeDelegator. Nice!

Insanely, I agree with you Ayende. If this changes in vNext, you'll just have to come up with another unnatural hack. Still better than Profiling APIs by any means.

Frank Quednau
11/24/2009 03:51 PM by
Frank Quednau

Yes, this looks more doable.

@Omer nice Fermat analogy there :)

This hack just goes to show that there is no true privacy in such a system, simplest example being how easy it is to instantiate a type through a private constructor. I wonder how the CLR would want to avoid such things from happening. After all it needs to interrogate the type to answer whether it is assignable and Type is an abstract class in which you can override stuff.

Jay R. Wren
11/24/2009 04:14 PM by
Jay R. Wren

I CAN'T TAKE IT ANYMORE! I've spent too many hours on this already... well, ok... only 2.5, but that is too many for me.

Once Fabian suggested deriving from Type, I got past the IsAssignableFrom... And I'm using ContextBoundObject to return a RealProxy from Activator.CreateInstance, but I cannot get past the unable to cast transparent proxy to type IRunner

What is the solution?

Fabian Schmied
11/24/2009 05:02 PM by
Fabian Schmied

Jay, implement IRemotingTypeInfo on your real proxy, then the cast isn't an issue any longer.

Jay R. Wren
11/24/2009 05:22 PM by
Jay R. Wren

Fabian,

Thanks. I had that, but removed it when I went with a different approach.

... I'm learning WAY too much here :)

Omer Mor
11/24/2009 08:41 PM by
Omer Mor

I tried to implement the solution according to the comments, but I still fail to get past the "unable to cast transparent proxy" part.

Can you give me a hint?

This is my solution so far:

public class Program

{

private static void Main()

{

    var myType = new MyType

<assemblyrunner();

    var compositeRunner = new CompositeRunner(myType);

    compositeRunner.Execute();

}

}

public class MyProxyAttribute : ProxyAttribute

{

public override RealProxy CreateProxy(ObjRef objRef1, Type serverType, object serverObject,

                                      Context serverContext)

{

    Type classToProxy = typeof (AssemblyRunner).GetInterface("ThirdParty.IRunner");

    return new MyProxy(classToProxy);

}

}

[MyProxy]

public class MySubclass : ContextBoundObject

{

}

public class MyType{T} : TypeDelegator

{

private bool m_firstTime = true;


public MyType() : base(typeof (T))

{

}


public override Type UnderlyingSystemType

{

    get

    {

        if (m_firstTime)

        {

            m_firstTime = false;

            return typeof (T);

        }

        return typeof (MySubclass);

    }

}

}

public class MyProxy : RealProxy, IRemotingTypeInfo

{

public MyProxy(Type classToProxy) : base(classToProxy)

{

}


public bool CanCastTo(Type fromType, object o)

{

    return true;

}


public string TypeName { get; set; }


public override IMessage Invoke(IMessage msg)

{

    return msg;

}

}

Van
11/25/2009 03:38 AM by
Van

Done it, so as hard as I thought.

This challenge suddenly much easier without the generic type.

Basically, I use:

  • TypeDelegator to pass IsAssignableFrom

  • ContextBoundObject, ProxyAttribute and RealProxy to create an interceptor for the instance.

  • IRemotingTypeInfo to pass the type casting check.

All method call are then redirected to the real runner.

I don't see any chance that there will be any change that will cause this code not to run in next versions of .NET Framework.

TypeDelegator is used for the purpose it was created, that is delegating the type information.

Proxy is used to redirect messages, which is also what it was created for.

The only place that I used a real hack is in the GetInterfaces of TypeDelegator when I use StackFrame to see who called the method to return the appropriate result.

Alkampfer
11/26/2009 07:33 AM by
Alkampfer

So there is no solution for the original one? I was really curious on how you hacked the typeof(T) :P

Alk.

Comments have been closed on this topic.