5

I've made a function that processes an array of objects, process(Object[]). It works on any type including integers and floats so long as you box each element first. I'd like the function to take unboxed elements aswell, if only to box it itself before processing. How do I do that?

I could wrap with a few functions like process(int[]) and process(float[]) but that also seems a hassle. I've tried process(ValueType[]) but the compiler still selects process(Object[]).

I have C# 2.0, but if there is a nice solution for 3.0 I'd like to see it.

2
  • Are you trying to avoid boxing for syntactic reasons or for performance? Commented Sep 4, 2009 at 9:03
  • 1
    In order to answer this, we really need to know why you'd like this (in more detail). If you're just want the convenience of a single function, then Jon's answer below will work fine. If you're afraid of the overhead of boxing, well, as my answer says you'll likely need to do some work on the method body as well, since moving boxing to inside the method body isn't an automatic performance win. Commented Sep 5, 2009 at 11:09

5 Answers 5

7

How does the processing work? The most obvious method signature would be:

void ProcessArray<T>(T[] data)

That is literally "an array of an arbitrary type" - although the type needs to be known by the caller at compile-time.

However, depending on how you use the elements, you may still end up boxing. Maybe that's okay, given the part of your question which says: "if only to box it itself before processing". If you're happy enough with that, great :)

If you could give more details of what you're doing, we may be able to help more.

Sign up to request clarification or add additional context in comments.

Comments

0

Short answer: you can't (usefully) do this. Generics might seem to be a way around this, but as soon as you actually use the generically typed instance, you'll be passing it to methods that must accept class object - so it'll be boxed then (worse; it'll be boxed many times probably, depending on your code).

If you want to gain performance by avoiding boxing generically, you'll need to work on how you process these instances and ensure that all processing can occur in a type-safe manner despite the generic approach - and that's not easy (see Generic Numerics for example) and depends heavily on the processing you need to do.

4 Comments

The question explicitly says that he doesn't necessarily mind boxing within the method. You can usefully make a method accept any array (either using Array or a generic method) rather than having to create a new array of type object[] containing boxed copies. This could have benefits in both convenience and performance - having a single "live" boxed value at a time may well mean that they only survive in gen0, whereas boxing the whole array beforehand could end up pushing them into gen1.
It's possible, though not very plausible. Without seeing the code it's hard to say. In general, if performance is an issue to the extent that boxing plays a role, it doesn't strike me as wise to encourage repeated, unnecessary boxing and unboxing inside inner loops. In any case, the OP needs a more specific question.
A quick test here (evaluates int X(object o) {return o==null?0:o.GetHashCode();} on each array element) resulted in the int[] array taking a little less than twice as long as the object[] array containing boxed ints. including a GC.Collect in the benchmark raises overall timings but the object[] array is still faster. Of course, the "pre-boxed" array has a higher peak memory consumption...
by comparison, a non-generic method (that avoids the o==null check) dealing only with int[] arrays takes around half the time. I've no idea about the performance of int.GetHashCode - although you'd think it be blindingly fast.
0
 static void T(Array a)
    {
        if (a is int[])
            Console.WriteLine("Int");
        else
            if (a is float[])
                Console.WriteLine("Float");
            else
                ....

    }
    static void Main(){
        T(new int[]{30});
        float[] a = { 11 };
        T(a);
    }

3 Comments

This is no better that what the OP already has - the method T can't access the arrays elements without boxing.
(and to avoid confusion, I'd not use the name T for a method intended for use with many types - that's commonly used as a generic type parameter)
arrays in .net are type of System.Array. OP has misconception about arrays.
0

Boxing is implicit. For example:

using System;

namespace MyProg
{
  class Program
  {

    private static void ParamTest(params object[] args)
    {
      foreach (object o in args)
      {
        Console.Write(o);
        Console.WriteLine(", ");
      }
      Console.WriteLine();
    }

    static void Main(string[] args)
    {
      ParamTest(1, "abc", 2.4, new object());
    }
  }
}

will print

1,
abc,
2.4,
System.Object,

Not a generic in sight!

4 Comments

But you can't pass an array which is already a float[] for example.
True, but the question was asking about unboxed elements - which I thought would usually imply scalar types.
@Vinjay: I'm not sure what you mean. The fact that the OP talks about adding overloads for int[] and float[] suggests he wants to be able to accept that sort of value...
Maybe he does - let's wait to hear from him. I was only saying how I interpreted the question; I have been known to be wrong ;-)
0

Well, technically, it is possible. But it's not recommended, nor useful, for obvious reasons. Note that it performs badly too, and doesn't allow you pass arrays directly.

For curiosity and weirdness sake, here is the trick. It uses TypedReference and undocumented C# keywords.

public void Process(__arglist)
{
    var iterator = new ArgIterator(__arglist);
    do
    {
        TypedReference typedReference = iterator.GetNextArg();
        Type type = __reftype(typedReference);
        if (type == typeof(int))
        {
            int value = __refvalue( typedReference,int);
            // value is an int
        }
        else if (type == typeof(string))
        {
            string value = __refvalue( typedReference,string);
            // value is a string
        }
        else if (type == typeof(double))
        {
            double value = __refvalue( typedReference,double);
            // value is a double
        }
        // etc.
    }
    while (iterator.GetRemainingCount() > 0);
}

You can call the method like that:

Process(__arglist(45, "lol", 42.5));

It will not box/unbox values types. But, well, forget that...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.