English 中文(简体)
Casting generic object array to two types
原标题:
  • 时间:2009-11-09 16:41:01
  •  标签:
  • c#
  • casting

I ve got a method that receives an Object[] and then performs actions on that array.

At first I was passing in this array as an IEnumerable<T> however the T can be of two different types.

The T s will always have the same properties, even thought they re different types.

Is it possible to cast to a a type at runtime so that I can used the properties i know each one will contain?

So where as it s possible to do:

var dataObject = (IEnumerable<T>) dataArray;

Is it somehow possible to do:

var dataObject = (dataArray.GetType()) dataArray;
最佳回答

Are you able to modify the source code of the two T types? If so then you could make both of them either (a) inherit from a common base class or (b) implement a common interface.

Edit following comments...

To accomodate different types for the ID property you could use explicit interface implementation. This would allow you to expose the second object s ID as an Int64 when accessed via the common interface. (The ID property would remain accessible as an Int32 when not accessed through the interface.)

Unfortunately the explicit interface implementation will require more changes to your original types.

void YourMethod(IEnumerable<ICommonToBothTypes> sequence)
{
    foreach (var item in sequence)
    {
        Console.WriteLine(item.ID);
    }
}

// ...

public interface ICommonToBothTypes
{
    long ID { get; }
}

public class FirstType : ICommonToBothTypes
{
    public long ID
    {
        get { return long.MaxValue; }
    }
}

public class SecondType : ICommonToBothTypes
{
    // the real Int32 ID
    public int ID
    {
        get { return int.MaxValue; }
    }

    // explicit interface implementation to expose ID as an Int64
    long ICommonToBothTypes.ID
    {
        get { return (long)ID; }
    }
}
问题回答

You can just create it as an interface and have both of those classes implement the interface. Then take Interface[] as the parameter instead of Object and you avoid casting all together.

If the two objects in the array derive from the same base type (inheritance) then you can box all the objects in your array as base class objects and you can use them with no knowledge of which particular object it might be.

See here for Boxing and Unboxing information from Microsoft

This is in essence what polymorphism is about, handling objects with a common interface.

Either inheritance from the same base class or creating an interface that both objects support will suit in this case. You will then be able to specifically define your array with either the base class type or the interface type, rather than "object".

If it s impossible to have the different classes all inherit the same interface, you can get all the properties defined in an object as a collection and then their value through key lookup where the key is the name of the property. I d implement this as extension methods like so:

public static Dictionary<string, object> GetProperties<T>(this T instance)
   where T : class
{
  var values = new Dictionary<string, object>();
  var properties =
    instance.GetType().GetProperties(BindingFlags.Public   |
                                     BindingFlags.Instance |
                                     BindingFlags.GetProperty);

  foreach (var property in properties)
  {
    var accessors = property.GetAccessors();

    if ((accessors == null) || (accessors.Length == 0))
      continue;

    string key = property.Name;
    object value = property.GetValue(instance, null);

    values.Add(key, value);
  }

  return values;
}

Then you can just do like this to get the property values:

void DoStuff(object[] objects)
{
  foreach (var o in objects)
  {
    var properties = o.GetProperties();
    var value = properties["PropertyName"];
  }
}

It will all be untyped and you ll break things when renaming properties if you forget to also rename the lookup keys in the consuming code, but otherwise, this should work fine. However, the best solution is undoubtedly the one suggested by Luke (using interfaces).

If you have access to the sourcecode defining the two element types

You should introduce a new interace containing the common properties and let the other two interfaces inherit from your new base interface. In your method you then use IEnumerable.Cast(TResult) to cast all elements to the common interface:

var dataObject = dataArray as IEnumerable;
if (dataObject != null)
{
  var data = dataObject.Cast<ICommonBase>()
  // do something with data
}

If you can t modify the sourcecode defining the two element types:

Use the IEnumerable.OfType() to filter elements based on their known types, effectively creating two strongly typed collections. Next perform the same operations with each of them (most verbose approach).

var dataObject = dataArray as IEnumerable;
if (dataObject != null)
{
  var a = dataObject.OfType<InterfaceA>()
  // do something with a

  var b = dataObject.OfType<InterfaceB>()
  // do the same with b
}

A reference of the functions used can be found on msdn.

If you want to avoid code duplication your only chance will be to use something called duck typing.Phil Haack has a great article on it. The idea is to introduce a new interface that contains the common elements. You then duck-type the array elements, effectively making them comply to the new interface at runtime.

The last option would be using reflection and extracting property infos by common names and then access each elements data. This is the slowest and IMHO most cumbersome approach.





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签