views:

246

answers:

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.

A: 

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.

Eamon Nerbonne
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.
Jon Skeet
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.
Eamon Nerbonne
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...
Eamon Nerbonne
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.
Eamon Nerbonne
A: 
 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);
    }
adatapost
This is no better that what the OP already has - the method T can't access the arrays elements without boxing.
Eamon Nerbonne
(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)
Eamon Nerbonne
arrays in .net are type of System.Array. OP has misconception about arrays.
adatapost
A: 

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!

Vinay Sajip
But you can't pass an array which is *already* a `float[]` for example.
Jon Skeet
True, but the question was asking about unboxed elements - which I thought would usually imply scalar types.
Vinay Sajip
@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...
Jon Skeet
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 ;-)
Vinay Sajip
+1  A: 

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.

Jon Skeet
Downvoters: please add a comment or your downvote is pretty pointless...
Jon Skeet
I agree, what's with the downvote without any explanation?
Pop Catalin
A: 

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...

Romain Verdier