Working with Structs

If you have ever tried to perform even the simplest reflection invocation on structs with the standard .NET Reflection API, you'll know how hard it is. It usually involves all kinds of tricks to workaround the copy-by-assignment nature of struct. For example a naive implementation which modifies a field of a struct reflectively might actually work on the copy of the struct and leave the orginal struct unchanged!!

Fasterflect encapsulates the working with struct via the same API provided when working with other reference types, although developers have to be a little more conscious when dealing with structs. Fortunately, the rules to work with struct are very simple and once you grasp them, whatever things you can do with reference types in Fasterflect, you can apply to struct type too. With a little bit of more consciousness, you can even write code working seamlessly with both struct and reference type. In fact, a great amount of unit test cases for Fasterflect is written in a way that they work seamlessly with both reference and struct types.

Before we look at the rules, note that these apply for accessing instance members of struct types only. Static members need no special treatment because the copy-by-assignment has no role in static member accessing.

The magic behind working with structs are all enabled through the extension methods provided by ValueTypeExtensions.
namespace Fasterflect
{
    /// <summary>
    /// Extension methods for working with types.
    /// </summary>
    public static class ValueTypeExtensions
    {
        ///<summary>
        /// Returns a wrapper <see cref="ValueTypeHolder"/> instance if <paramref name="obj"/> 
        /// is a value type.  Otherwise, returns <paramref name="obj"/>.
        ///</summary>
        ///<param name="obj">An object to be examined.</param>
        ///<returns>A wrapper <seealso cref="ValueTypeHolder"/> instance if <paramref name="obj"/>
        /// is a value type, or <paramref name="obj"/> itself if it's a reference type.</returns>
        public static object WrapIfValueType( this object obj )
        {
            return obj.GetType().IsValueType ? new ValueTypeHolder( obj ) : obj;
        }

        ///<summary>
        /// Returns a wrapped object if <paramref name="obj"/> is an instance of <see cref="ValueTypeHolder"/>.
        ///</summary>
        ///<param name="obj">An object to be "erased".</param>
        ///<returns>The object wrapped by <paramref name="obj"/> if the latter is of type <see cref="ValueTypeHolder"/>.  Otherwise,
        /// return <paramref name="obj"/>.</returns>
        public static object UnwrapIfWrapped( this object obj )
        {
            var holder = obj as ValueTypeHolder;
            return holder == null ? obj : holder.Value;
        }

        /// <summary>
        /// Determines whether <paramref name="obj"/> is a wrapped object (instance of <see cref="ValueTypeHolder"/>).
        /// </summary>
        /// <param name="obj">The object to check.</param>
        /// <returns>Returns true if <paramref name="obj"/> is a wrapped object (instance of <see cref="ValueTypeHolder"/>).</returns>
        public static bool IsWrapped( this object obj )
        {
            return obj as ValueTypeHolder != null;
        }
    }
}

When you are sure that an object is of struct type (or if there's a possibility that it's a struct type), you need to invoke WrapIfValueType before using it with Fasterflect. This method creates a wrapper object if the given object is indeed struct type and does no harm if it's not. So it's one of the secret sauces towards working seamless with both struct and reference types. The second secret sauce is the method UnwrapIfWrapped which returns the original struct (so that you can pass to another non-Fasterflect method, cast it etc.) or does no harm if it's not a wrapped object.

var obj = typeof(SomeTypeWhichMightBeStruct).CreateInstance();
var wrapped = obj.WrapIfValueType(); // wrap in case it's struct type, no effect if it's not 
wrapped.SetFieldValue("afield", 10); // now, just use what you've already know about Fasterflect to access the object's members
// more Fasterflect operations on struct here...
obj = wrapped.UnwrapIfWrapped(); // when you finish, do this to get back the original struct, no effect if it's not a struct type

While the above 2 methods are sufficient for dealing with struct type, the last method IsWrapped() is an additional helper to quickly check whether a given object is a wrapped object (produced by WrapIfValueType) or not. That's everything you need to know about dealing with structs in Fasterflect.

Last edited Mar 11, 2010 at 1:09 AM by buunguyen, version 3

Comments

steenreem Feb 24, 2015 at 7:57 AM 
Hi there,

I'm a bit dissapointed that I can't use fasterflect property setting/getter as a drop-in replacement for regular reflection usage. This is due fasterflect's handling of struct types. It assumes I'm using this ValueTypeHolder wrapper, which I'm not. Also I don't intent to use the ValueTypeHolder.

I'd rather that fasterflect defaults to regular slow reflection for properties of struct types than that it crashes. (It crashes with an InvalidCastException because it tries to cast to ValueTypeHolder which I'm not using)

Cheers,
Remy.