Why Is Typedreference Behind the Scenes? It's So Fast and Safe... Almost Magical!

Why is TypedReference behind the scenes? It's so fast and safe... almost magical!

Short answer: portability.

While __arglist, __makeref, and __refvalue are language extensions and are undocumented in the C# Language Specification, the constructs used to implement them under the hood (vararg calling convention, TypedReference type, arglist, refanytype, mkanyref, and refanyval instructions) are perfectly documented in the CLI Specification (ECMA-335) in the Vararg library.

Being defined in the Vararg Library makes it quite clear that they are primarily meant to support variable-length argument lists and not much else. Variable-argument lists have little use in platforms that don't need to interface with external C code that uses varargs. For this reason, the Varargs library is not part of any CLI profile. Legitimate CLI implementations may choose not to support Varargs library as it's not included in the CLI Kernel profile:

4.1.6 Vararg

The vararg feature set supports variable-length argument lists and runtime-typed pointers.

If omitted: Any attempt to reference a method with the vararg calling convention or the signature encodings associated with vararg methods (see Partition II) shall throw the System.NotImplementedException exception. Methods using the CIL instructions arglist, refanytype, mkrefany, and refanyval shall throw the System.NotImplementedException exception. The precise timing of the exception is not specified. The type System.TypedReference need not be defined.

Update (reply to GetValueDirect comment):

FieldInfo.GetValueDirect are FieldInfo.SetValueDirect are not part of Base Class Library. Note that there's a difference between .NET Framework Class Library and Base Class Library. BCL is the only thing required for a conforming implementation of the CLI/C# and is documented in ECMA TR/84. (In fact, FieldInfo itself is part of the Reflection library and that's not included in CLI Kernel profile either).

As soon as you use a method outside BCL, you are giving up a bit of portability (and this is becoming increasingly important with the advent of non-.NET CLI implementations like Silverlight and MonoTouch). Even if an implementation wanted to increase compatiblility with the Microsoft .NET Framework Class Library, it could simply provide GetValueDirect and SetValueDirect taking a TypedReference without making the TypedReference specially handled by the runtime (basically, making them equivalent to their object counterparts without the performance benefit).

Had they documented it in C#, it would have had at least a couple implications:

  1. Like any feature, it may become a roadblock to new features, especially since this one doesn't really fit in the design of C# and requires weird syntax extensions and special handing of a type by the runtime.
  2. All implementations of C# have to somehow implement this feature and it's not necessarily trivial/possible for C# implementations that don't run on top of a CLI at all or run on top of a CLI without Varargs.

Practical uses of TypedReference

Are there any practical uses of the TypedReference struct that you would actually use in real code?

Yes. I'd use them if I needed interoperability with C-style variadic methods.

Why do these overloads exist?

They exist for interoperability with callers who like to use C-style variadic methods.

Keep a TypedReference alive out of method block without returning it

What are you trying to do exactly? Locals are on stack and arguments are too depending on the calling convention. Storing or returning the address of a local or argument is not good because it will get overriden. There is no way to prevent them from being overriden besides like not calling methods.

If you turn on unmanaged debugging you can use the memory debugger and register window to see what is going on.

Here is simpler to understand C example. Why does print not display the correct value. Because when the print function gets invoked its stack frame overwrites the value.

int* bad(int x, int y)
{
int sum = x + y;
return ∑
};

int* bad2(int x, int y)
{
x += y;
return &x;
}

int _tmain(int argc, _TCHAR* argv[])
{
int* sum1 = bad(10, 10);
int* sum2 = bad(100, 100);
printf("%d bad", *sum1); // prints 200 instead of 20

sum1 = bad2(10, 10);
sum2 = bad2(100, 100);
printf("%d bad", *sum1); // prints 200 instead of 20

return 0;
};

Cant get the clr to hold on for it longer. One thing you can do is get the variable on the stack pushed further out. Below is an example. This is all bad though :(

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Xml.Linq;
using System.Runtime.InteropServices;

namespace Bad
{
class Program
{
static void Main(string[] args)
{
Action a = () => Console.WriteLine("test");
Horror h = new Horror();
h.Fix(new Big(), ref a, new Big());
h.Clear();
Console.WriteLine();
}
}
[StructLayout(LayoutKind.Sequential, Size = 4096)]
struct Big
{
}
unsafe class Horror
{
void* _ptr;

static void Handler()
{
Console.WriteLine("Hello world.");
}

public void Fix(Big big, ref Action action, Big big2)
{
action += Handler;
var tr = __makeref(action);
_ptr = (void*)&tr;
}

public void Clear()
{
var tr = *(TypedReference*)_ptr;
__refvalue(tr, Action) -= Handler;
}
}
}

Avoiding boxing in generic blackboard

While I wouldn't want to do this (and I'd need persuading that the cost of boxing is really going to be significant), you could have your multiple stores and have a variable of type Dictionary<object, T> in your method - that way I believe you'd avoid boxing:

T GetValue<T>(object key)
{
Dictionary<object, T> store;
if (typeof(T) == typeof(int)
{
store = (Dictionary<object, T>) (object) intStore;
}
else if (typeof(T) == typeof(float))
{
store = (Dictionary<object, T>) (object) floatStore;
}

// etc - with a default or an error case for unhandled types.
return store[key];
}

Note that the double cast here is necessary to keep the compiler happy, but it doesn't involve boxing.

C# Reflection: What is the difference between FieldInfo.SetValue() and FieldInfo.SetValueDirect()?

SetValueDirect takes a TypedReference parameter, and as such it is to be used when varargs parameter interoperability is needed. This performs no boxing/unboxing of values and no access checking. It runs faster than SetValue.

SetValue on the other hand operates on pretty much everything else. It performs boxing/unboxing of the parameter where needed, and also checks for access.

There are SO's that discuss TypedReferences here and here - there are code examples in both.

What is meant by a type being output-safe or input -safe?

This part of the spec -- you are referring to section 13.1.3.1 of the C# 4 specification, which perhaps you ought to have mentioned somewhere in your question -- is unfortunate, and I apologize. Mads and I meant well, but I've never been thrilled with how this part of the spec turned out.

We were attempting to come up with a more intuitive and easy-to-understand description of "covariantly valid", "contravariantly valid" and "invariantly valid" for the C# specification; the original draft specification was written with these terms, which people found confusing. The result was not actually much easier to understand, and it contained some errors.

My advice is: if you need to understand the precise rules for what makes a variant interface valid or invalid then you should read my notes on the subject which are here:

http://blogs.msdn.com/b/ericlippert/archive/2009/12/03/exact-rules-for-variance-validity.aspx

That is essentially an annotated transcription of the notes that I made while implementing the feature. These notes are much more detailed and hopefully more clear than the text that ended up in the spec.

You might also want to read the relevant section in the CLI specification; my notes were based directly on a thorough reading of it.

To actually answer your question: the idea behind "output unsafety" and "input unsafety" is essentially that if you have:

interface I<in T>
{
T M();
}

Then T cannot be safely used in output positions. Suppose we allowed this; doing so violates type safety:

class C : I<Animal> 
{ public Animal M() { return new Giraffe(); } }
...
I<Animal> ia = new C<Animal>();
I<Tiger> it = ia; // Contravariant!
Tiger t = it.M(); // We just assigned a giraffe to a variable of type tiger.

That violates type safety, so we say that T is "output unsafe", and therefore this interface is not valid, because plainly T is used in an output position. Similarly for "input unsafe":

interface I<out T>
{
void M(T t);
}

You could make an I<Giraffe> that takes a giraffe, convert it to I<Animal> and pass in a tiger to M, which is not safe. T is input-unsafe, and therefore this interface, which uses T in an input position, is not valid.

x.ToString() or x + ?

If you compile your x + "" code and than decompile it back or look into IL, you'll notice that compiler replaces it with string.Concat call:

x1 = input + "";

becomes

x1 = string.Concat(input);

And if you look at what string.Concat does you'll see that it indeed does call ToString, but it also makes additional null check on the input:

public static String Concat(Object arg0) {
Contract.Ensures(Contract.Result<String>() != null);
Contract.EndContractBlock();

if (arg0 == null)
{
return String.Empty;
}
return arg0.ToString();
}

So, as you can see it's more code to exeucte than just ToString call, which means it can't be faster than just ToString call.

They might be really close if JIT is able to optimize the null check away in some cases, but in general + "" will be slower than ToString call.

But you should also be aware that the two do not have exactly the same behavior: ToString on null will throw NullReferenceException, while + "" will return an empty string.

Save a object reference for future usage using ref keyword

Sounds like you're looking for TypedReference and the __makeref keyword.

Warning: they're poorly documented and not in the standardized part of C#.

There's a lot more information in this question.

Type safe objectdatasources

No, all binding is with strings holding property/column names.

Maybe consider a Test project (Unit testing). That would be my answer to the last part.

Invoke generic method with the generic type from a System.Type

Update 3: OK, since you're fine with an ugly solution, you may want to check out the undocumented __refvalue and __makeref keywords.


It seems your issue is that you want to be able to specify the type for a ref object parameter to be converted or changed to.

The problem with that is that you can't just arbitrarily assign a variable of any type T to a string, for example. So you'd need to pass in a ref object or ref string for that to work at all.

It looks to me like oberfreak nailed down what you were trying to achieve (I originally failed to notice you had initialized o as a string and thus clearly wanted its actual type to influence the behavior of the Test2 function). His answer has the right approach for you.

Update: you mention in a comment that what you're trying to do is have dynamic behavior which could be achieved using a dictionary. I'm guessing that looks something like this?

Update 2: updated this example based on your updated example.

public static class TypeHandler // note: get rid of generic T parameter
{
delegate void ProcessDelegate(ref object obj); // again, not generic

static Dictionary<Type, ProcessDelegate> processors = new Dictionary<Type, ProcessDelegate>()
{
{ typeof(string), (ref object obj) => { obj = "Hello, world!"; } }
// etc.
};

public static void Process(ref object obj)
{
processors[obj.GetType()].Invoke(ref obj);
}
}

That should work. But you can't really achieve the same thing with generics because there's no way to do this (as you know):

//          not allowed
// |
// -----------
// | |
TypeHandler<o.GetType()>.Process(ref o);

If there were, then you'd be all set. But the only possible way you can do that is by using reflection, which is ugly and expensive for something like this and would clearly defeat your intention of keeping this simple and ensuring good performance.



Related Topics



Leave a reply



Submit