简体   繁体   中英

Can 'this' argument of an instance method be untyped (i.e. System.Object)?

I'm using System.Reflection.Emit 's TypeBuilder to emit a bunch of custom .NET classes with instance methods. For example:

public class EmittedClass
{
    public bool TryGetName(out string value)
    {
        ...
    }

    public bool TryGetAge(out int value)
    {
        ...
    }
}

All methods follow the same signature that can be described by a generic delegate:

public delegate bool TryGetter<T>(out T value);

Of course, I'd like be able to explicitly specify the target instance on the call site, like this:

var instance = InstanceFactory.CreateInstance();
var tryGetName = InstanceFactory.CreateTryGetter<string>("Name");
string name;
if (tryGetName(instance, out name)) // Problem here.
{
    ...
}

For this to work, I need to make the delegate into a so called open delegate :

public delegate bool TryGetter<T>(object instance, out T value);

Since I don't have the compile-time Type of the target instance, I need to pass it as System.Object . However, this breaks at runtime, because the instance method expects its 'this' to be of the declaring class's type. Ouch.

The solution I'm currently using is to create an intermediate lambda expression that takes the input object, performs a runtime cast to target type, and then proceeds to invoke the target method. It works, but I feel uneasy about having this kludge in the middle.

The question is: can I somehow change my emitted methods so that their this arguments will accept any System.Object and still keep my emitted code verifiable?

If not, I guess I can still avoid the intermediate lambda by making the methods static and performing the casts in their bodies. Ideally however, I'd like to avoid the casting altogether, but I suspect this can't stand in the managed world due to the way CIL verification and metadata loading work.

Just wondering if someone more knowledgeable about CIL can give me advice on this subject. Thanks!

UPDATE: The problem I'm trying to solve

Classes have properties, which are normally backed by fields. This is fine, if instances of said classes are expected to only maintain a single state at a time. With state I mean the combination of values in instance fields at a given time.

My business requirement is to implement an alternative storage to plain fields that will enable a single class instance to have more than one state at the same time. A side goal of this requirement is to make this as efficient as possible, both memory- and speed of access-wise.

The core idea behind my approach is to create a mirror image of the business class using System.Reflection.Emit and have the business class maintain a set of those instances, each corresponding to a given state. The getters and setters of the business class then naturally have to be wired to the appropriate methods on the state instance. There are a lot more details involved, but this is the core idea.

I hope the explanation helps understand the reason why I'm asking this question. I appreciate it may seem like over-engineering to many, but other alternatives that involve no System.Reflection.Emit are just plain too slow and resource hungry. I can't have that.

Considering your requirements wouldn't a library like ValueInjecter or AutoMapper reduce all the boilerplate code of copying from one large business object to different state objects? Even if it's not exactly what you want, perhaps they might be of some inspiration for your task.

You don't need to represent the 'this' type in the delegate signature.

That is given a delegate definition like so:

public delegate bool TryGetter<T>(out T value);

Any variable of the form:

TryGetter<T> x;

Can hold either:

  1. An instance method on type R with signature bool Foo(out T value) , together with an object instance of type R.
  2. A static method with signature static bool Foo(out T value)
  3. A static method with signature static bool Foo(R object, out T value) given an object instance of type R.

The third form is called delegate currying, and allows a static method with N+1 arguments to behave as if it was an instance method with N arguments (only the first argument may be curried however).

So, the interface you want is:

var instance = InstanceFactory.CreateInstance();
var tryGetName = InstanceFactory.CreateTryGetter<string>(instance,"Name");

Then you can do: tryGetName() to return the value.

You would probably want to go with case #3, where you generated a DynamicMethod with the signature bool TryGetWhatEver(TheTypeOfInstance obj, out WhatEver x) , and then create a TryGetter<WhatEver> .


However I'm still curious as to why you would need to do this. Unless you are dynamically generating big chunks of your app (like rails), this seems like it would be overly complicated.

Reflection.Emit is waaaay overkill for creating an internal storage mechanism to house copies of your property values. Something as simple as a few Dictionary would be a sufficient way of storing various 'states' of propertyname->value mappings.

Just use dynamic :

dynamic instance = InstanceFactory.CreateInstance();
var tryGetName = InstanceFactory.CreateTryGetter<string>("Name");
string name;

// Should work if “instance” is of the right type *at runtime*
if (tryGetName(instance, out name))
{
    ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM