次の方法で共有


Overcoming the Limitations of Anonymous Types

It seems clear that the new anonymous types feature for C# 3.0 is
going to be very useful, however there are a few limitations that come
along with them: limited scope and read-only properties.

Anonymous types have a method scope.  This means that if you pass an
anonymous type outside the containing method boundary, then you have to
cast it as an object.  For example:

 static void ContainingMethod()
{
  // Create an anonymous type
  var anondata = new
  {
    IntegerVal = 1,
    DoubleVal = 2.0D,
    DateTimeVal = DateTime.Now,
    StringVal = "some string"       
  };

  // Pass the type to a method
  ExternalMethod(anondata);
}

static void ExternalMethod(object data)
{
  // We can't access the properties and
  // we can't cast it to anything of use
  // because it is anonymous
  data.(???)
}

We can't access any of the properties of the type because it has
been cast to an object.  So, let's see what the type looks like to the
external method (just a small change to the second method from above):

 static void ExternalMethod(object data)
{
  Console.WriteLine(data.GetType());
}


This outputs something like:

<>f__AnonymousType0`4[System.Int32,System.Double,System.DateTime,System.String]

This is the symbol that the compiler picked for the anonymous type
that we declared. There are a few interesting bits of information here.
The "0" after "<>f__AnonymousType" is a zero-based index that
represents each unique anonymous type definition. In our case, this is
the first anonymous type that has been defined within the assembly. The
next number, "4", represents the number of properties that they type
contains. Following that is a list of the types for the properties.

What if we need to use the anonymous type outside of the containing
method? Well, the best practice would be to create a standard named
class and use that instead of the anonymous type. But what if you
really need (or just want) to pass an anonymous type and access the
properties from outside the containing method?  Well, all you need is a
little reflection (again using the example from above):

 static void ContainingMethod()
{
  var anondata = new
  {
    IntegerVal = 1,
    DoubleVal = 2.0D,
    DateTimeVal = DateTime.Now,
    StringVal = "some string"       
  };

  ExternalMethod(anondata);
}

static void ExternalMethod(object data)
{
  // Get the type that was passed in
  Type t = data.GetType();
  // Get a list of the properties
  PropertyInfo[] piList = t.GetProperties();
  // Loop through the properties in the list
  foreach (PropertyInfo pi in piList)
  {
    // Get the value of the property
    object o = pi.GetValue(data, null);
    // Write out the property information
    Console.WriteLine("{0} ({1}): \t{2}", pi.Name, o.GetType(), o.ToString());
  }
}

This outputs:

IntegerVal (System.Int32):      1
DoubleVal (System.Double):      2
DateTimeVal (System.DateTime):  9/7/2007 8:06:04 PM
StringVal (System.String):      some string

As you can see, by using reflection we can access the properties of
the anonymous type from another method or class. Again, it would
probably be better to just write a named class to do this, but this
example is intended to show that it is possible and fairly easy to pass
data using only anonymous types.

Another limitation of anonymous types is that the properties are
read-only. I really don't see many instances where you would need to
change the values inside of an anonymous type, but if you ever wanted
to there is a way to do that as well. And you guessed it, it involves
using reflection again:

 static void ContainingMethod()
{
  // Create the anonymous type
  var anondata = new
  {
    IntegerVal = 1,
    DoubleVal = 2.0D,
    DateTimeVal = DateTime.Now,
    StringVal = "some string"
  };

  // This is the underlying private field name 
  // for the IntegerVal property
  string fieldName = "<IntegerVal>i__Field";

  // Get the type
  Type t = anondata.GetType();
  // Get the field information using reflection
  FieldInfo fi = t.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
  // Set the field using reflection
  fi.SetValue(anondata, 1234);

  // Output the value from the original anonymous type property
  Console.WriteLine("IntegerVal = {0}", anondata.IntegerVal.ToString());
}

This outputs: 

IntegerVal = 1234

The interesting thing here is that we cannot set the read-only
property using reflection because there is no setter. But we can change
the underlying private field for the property. The field name seems to
follow the convention "<PropName>i__Field" where 'PropName' is
the name that we used for the property. This name can be gathered by
using reflection and enumerating the fields (using the GetFields method
of the Type object).  So as you can see, we can change the value of the
property of an anonymous type if we really need to.

To sum this
up, using reflection we can do some pretty cool things with anonymous
types.  For most scenarios it would be easier and more practical to
create named classes to pass and alter data, but this is meant to show
that we can break the rules a little if we really want to.

Comments

  • Anonymous
    September 07, 2007
    The comment has been removed