53

Is it possible to cast out param arguments in C#? I have:

Dictionary<string,object> dict;  // but I know all values are strings
string key, value;

Roughly speaking (and if I didn't have static typing) I want to do:

dict.TryGetValue(key, out value);

but this obviously won't compile because it "cannot convert from 'out string' to 'out object'".

The workaround I'm using is:

object valueAsObject;
dict.TryGetValue(key, out valueAsObject);
value = (string) valueAsObject;

but that seems rather awkward.

Is there any kind of language feature to let me cast an out param in the method call, so it does this switcheroo for me? I can't figure out any syntax that'll help, and I can't seem to find anything with google.

8 Answers 8

31

I don't know if it is a great idea, but you could add a generic extension method:

    static bool TryGetTypedValue<TKey, TValue, TActual>(
        this IDictionary<TKey, TValue> data,
        TKey key,
        out TActual value) where TActual : TValue
    {
        if (data.TryGetValue(key, out TValue tmp))
        {
            value = (TActual)tmp;
            return true;
        }
        value = default(TActual);
        return false;
    }
    static void Main()
    {
        Dictionary<string,object> dict
            = new Dictionary<string,object>();
        dict.Add("abc","def");
        string key = "abc", value;
        dict.TryGetTypedValue(key, out value);
    }
Sign up to request clarification or add additional context in comments.

7 Comments

This is an interesting bit of syntactic sugar.
+1 never done this on a dictionary, but have some very similar helper ext methods.
The only thing I'd possibly consider changing would be the handling of the case where the value is found but is of the wrong type. Instead of throwing, I might want it to just return false. Then again, I might not. I'd have to think this over, but it's worth mentioning.
That's pretty cool. I don't think I'll use that (yet -- since it's way overkill for my case), but it's definitely cool.
You can remove the first line in TryGetTypedValue and do just TryGetValue(key, out TValue tempValue))
|
23

I spy with my little eye an old post that was still active a month ago... Here's what you do:

public static class DictionaryExtensions
{
    public static bool TryGetValueAs<Key, Value, ValueAs>(this IDictionary<Key, Value> dictionary, Key key, out ValueAs valueAs) where ValueAs : Value
    {
        if(dictionary.TryGetValue(key, out Value value))
        {
            valueAs = (ValueAs)value;
            return true;
        }

        valueAs = default;
        return false;
    }
}

And because compilers are great, you can just call it like this:

dict.TryGetValueAs(key, out bool valueAs); // All generic types are filled in implicitely! :D

But say you're not creating a blackboard AI and just need to call this operation the one time. You can simply do a quicksedoodle inliner like this:

var valueAs = dict.TryGetValue(key, out var value) ? (bool)value : default;

I know these answers have been given already, but they must be pretty old because there is no cool hip modern inlining going on to condense these methods to the size we really want: no more than 1 line.

Comments

7

I used Marc's extension method but added a bit to it.

My problem with the original was that in some cases my dictionary would contain an int64 whereas I would expect an int 32. In other cases the dictionary would contain a string (for example "42") while I would like to get it as an int.

There is no way to handle conversion in Marc's method so I added the ability to pass in a delegate to a conversion method:

internal static bool TryGetTypedValue<TKey, TValue, TActual>(
        this IDictionary<TKey, TValue> data,
        TKey key,
        out TActual value, Func<TValue, TActual> converter = null) where TActual : TValue
    {
        TValue tmp;
        if (data.TryGetValue(key, out tmp))
        {
            if (converter != null)
            {
                value = converter(tmp);
                return true;
            }
            if (tmp is TActual)
            {
                value = (TActual) tmp;
                return true;
            }
            value = default(TActual);
            return false;
        }
        value = default(TActual);
        return false;
    }

Which you can call like this:

int limit;
myParameters.TryGetTypedValue("limitValue", out limit, Convert.ToInt32)

Comments

5

No, there is no way around that. The out parameter must have a variable that matches exactly.

Using a string reference is not safe, as the dictionary can contain other things than strings. However if you had a dictionary of strings and tried to use an object variable in the TryGetValue call, that won't work either even though that would be safe. The variable type has to match exactly.

Comments

3

If you know all values are strings use Dictionary<string, string> instead. The out parameter type is set by the type of the second generic type parameter. Since yours is currently object, it will return an object when retrieving from the dictionary. If you change it to string, it will return strings.

1 Comment

The Dictionary<string,object> is provided by an interface I'm using. I know all values are strings for my use of it, because that's what I'm filling it with here.
3

No, you can't. The code inside the method is directly modifying the variable passed to it, it is not passed a copy of the content of the variable.

Comments

0

It is possible by using the Unsafe.As<TFrom, TTo>(ref TFrom source) method to do the cast inline.

var dict = new Dictionary<string, int>
{
    ["one"] = 1,
    ["two"] = 2,
    ["three"] = 3,
};

long result = 0;
dict.TryGetValue("two", out Unsafe.As<long, int>(ref result));

Depending on which platform you are on, this may require you to add a reference to System.Runtime.CompilerServices.Unsafe.

Comments

0

I'm sure that you have moved on by now (15 years later), but for future references, today it can be done like this:

// Not using TryGetValue
var value = dict[key] as string;
    
// Using trygetValue
if(dict.TryGetValue(key, out obj) && obj is string value) 
{
}

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.