views:

92

answers:

6

Is there a way to implement a generic implicit or explicit converter for anything to an array of anything, something like this:

public static implicit operator T[](T objToConvert)
{
    return new T[] { objToConvert };
}
+1  A: 

Operator overloading methods have to live inside the class they are overriding operators for (one side or the other). Since "T" is not defined, I don't see how this can be accomplished.

Kirk Woll
I'll give this an hour or two for Jon Skeet to come in here and wow us with some arcane solution involving reverse engineering the compiler or something, short of that happening I'll hand you the answer.
Jimmy Hoffa
Ha ha, nice comment. ;)
Kirk Woll
+7  A: 

No. The closest I can think of is an extension method:

public static T[] AsArray<T>(this T instance) 
{
    return new T[]{instance};
}

Use as:

var myArray = myInstnace.AsArray();
driis
This sort of thing is a double-edgedsword. On one hand, having a generic method that can do this can be useful - particularly when used with libraries like LINQ, where sometimes you want individual elements to be easily treated as sequences. On the other hand, it can be a pain because generic extension method like this show up for every type in Intellisense, and can be confusing to other developers. It's also susceptible to abuse. In general, being conservative about creating globally applicable extension methods is a good thing. With respect to `AsArray` - I'm on the fence.
LBushkin
@LBushkin, I agree. I don't have an AsArray method in my current project; instead I use the array initialization syntax. Depending on the OP's situation and usage, it might make sense to have it as an extension method, though.
driis
I don't actually have a usage case, I was just wondering if there's any way to do it.
Jimmy Hoffa
+7  A: 

Note that you can omit the type name from the array constructor, which means the syntax is fairly clean, even with a long type name:

ReallyLongAndAwkwardTypeName value;
MethodThatTakesArray(new[] {value}); 
Dan Bryant
Didn't know that, great shortcut!
Jimmy Hoffa
I didn't know this either until ReSharper pointed it out. :)
Dan Bryant
A: 

You can do it using normal method:

public static T[] ToArray<T>(T objToConvert) {
    return new T[] { objToConvert };
}

I don't think you can define generics operator. Note, anyway, that the compiler is sufficient cleaver to guess the type of the generic param, so you can use:

var aString="";
var aStringArray=ToArray(aString);

aStringArray is defined as a string array even if you don't specify the generic param.

Andrea Parodi
A: 

I was trying to think of situations where you might really use an implicit conversion to array. I started to wonder if many of the situations where you would want to do this could be alleviated by use of the params keyword.

The main situation that I could think of was that you had a single item of something and wanted to pass it to a function that takes an array as a parameter:

    static void Main(string[] args)
    {
        string x = "I'm just a poor variable.  Nobody loves me.";

        Stickler.IOnlyTakeArrays_Rawr111(x); // won't go in!  square peg, round hole, etc.

        // *sigh* fine.
        Stickler.IOnlyTakeArrays_Rawr111(new[] { x });
    }

    class Stickler
    {
        public static void IOnlyTakeArrays_Rawr111(string[] yum)
        {
            // ...
        }
    }

Hopefully in this situation the author of the method that you want to call has choosen to use the params keyword to allow you to pass your variable without wrapping it in an array:

    class DataConcierge
    {
        public static T Create<T>(int id)
        {
            // ...
        }

        public static void Save<T>(params T[] items)
        {
            // ...
        }
    }

    static void Main(string[] args)
    {
        var customer = DataConcierge.Create<Customer>(123);

        // ...

        DataConcierge.Save(customer); // this works!

        //----------------------------------------------------
        // or
        //----------------------------------------------------

        var customers = new Customer[]
        {
            DataConcierge.Create<Customer>(123),
            DataConcierge.Create<Customer>(234),
            DataConcierge.Create<Customer>(345),
        };

        // ...

        DataConcierge.Save(customers); // this works too!
    }

Of course, this doesn't really help you in situations where you need convert a variable to a single item array but not as a parameter to a method or in situations where the author of the method didn't use the params keyword.

But what kind of situation would the former be? Assigning an array to a property? Psh. How often does that happen?

And the latter? If the author didn't use the params keyword when they could have, then send them an email complaining about it. If the author is yourself, feel free to be extra belligerent in the email.

Hopefully you can tell that I'm being facetious. Seriously, though, are there any other common usage situations that you can think of where the params keyword would not be applicable?

** Disclaimer: I don't advocate excessive use of the params keyword. Use it if you think you should, but don't take my post to mean that you should always use the params keyword whenever you can.

Dr. Wily's Apprentice
So what you're saying is that all method sigs should just be SomeMethod(params object[] parameters) ? I agree! What's facetious mean? It's got the word face in it, are you trying to imply i have an ugly face?
Jimmy Hoffa
A: 

In the past I've used the concept of a "Conductor" (my own name for it), which is just a class/struct that provides access to an underlying value.

The concept is useful for abstracting the access to a particular value retrieved from somewhere. For example, if you wanted to abstract access to a particular value in a dictionary, you could create a Conductor object that held a reference to the dictionary and the appropriate key for that value. You can also use this concept to easily implement rollback for serializable classes or for value types, though for that you'd need to add Rollback and Commit methods to the Conductor class/struct.

Below is an example of how you can use implicit conversions from T to Conductor and from Conductor to T[] in order to (sort of) achieve what you want.

    static void Main(string[] args)
    {
        // implicit conversion here from Customer to Conductor<Customer>
        Conductor<Customer> conductor = DataConcierge.Create<Customer>(123);

        if (conductor.HasValue)
        {
            Console.WriteLine("I got a customer with Id {0}!", conductor.Value.Id);

            // implicit conversion here from Conductor<Customer> to Customer[]
            DataConcierge.Save<Customer>(conductor);
        }
    }



public struct Conductor<T> : IConductor<T>, IEquatable<T>, IEquatable<Conductor<T>>, IEquatable<IConductor<T>>
{
    private T _Value;

    public Conductor(T value)
    {
        this._Value = value;
    }

    public T Value
    {
        get { return this._Value; }
        set { this._Value = value; }
    }

    public bool HasValue
    {
        get { return this._Value != null; }
    }

    public T GetValueOrDefault()
    {
        if (this.HasValue)
            return this.Value;
        else
            return default(T);
    }

    public T GetValueOrDefault(T @default)
    {
        if (this.HasValue)
            return this.Value;
        else
            return @default;
    }

    public bool TryGetValue(out T value)
    {
        if (this.HasValue)
        {
            value = this.Value;
            return true;
        }
        else
        {
            value = default(T);
            return false;
        }
    }

    public T[] AsArray()
    {
        return new T[] { this._Value };
    }

    public static implicit operator Conductor<T>(T value)
    {
        return new Conductor<T>(value);
    }

    public static implicit operator T(Conductor<T> conductor)
    {
        return conductor.Value;
    }

    public static implicit operator T[](Conductor<T> conductor)
    {
        return conductor.AsArray();
    }

    public bool Equals(T other)
    {
        var otherEquatable = other as IEquatable<T>;
        if (otherEquatable != null)
            return otherEquatable.Equals(this.Value);
        else
            return object.Equals(this.Value, other);
    }

    public bool Equals(Conductor<T> other)
    {
        if (other.HasValue)
            return this.Equals(other.Value);
        else
            return !this.HasValue;
    }

    public bool Equals(IConductor<T> other)
    {
        if (other != null && other.HasValue)
            return this.Equals(other.Value);
        else
            return !this.HasValue;
    }

    public override bool Equals(object obj)
    {
        if (obj == null)
            return !this.HasValue;

        var conductor = obj as IConductor<T>;
        if (conductor != null)
        {
            if (conductor.HasValue)
                return this.Equals(conductor.Value);
            else
                return !this.HasValue;
        }

        return object.Equals(this.Value, obj);
    }

    public override int GetHashCode()
    {
        if (this.HasValue)
            return this.Value.GetHashCode();
        else
            return 0;
    }

    public override string ToString()
    {
        if (this.HasValue)
            return this.Value.ToString();
        else
            return null;
    }
}
Dr. Wily's Apprentice