views:

66

answers:

1

Hey,

I was wondering if it's possible to run the following code but without the unboxing line:-

t.Value = (T)x;

Or maybe if there is another way to do this kind of operation?

Here is the full code:-

public class ValueWrapper<T>
{
    public T Value { get; set; }
    public bool HasValue { get; set; }

    public ValueWrapper()
    {
        HasValue = false;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Dictionary<string, object> myDictionary = new Dictionary<string, object>();

        myDictionary.Add("key1", 6);
        myDictionary.Add("key2", "a string");

        var x2 = GetValue<int>(myDictionary, "key1");
        if (x2.HasValue)
            Console.WriteLine("'{0}' = {1}", "key1", x2.Value);
        else
            Console.WriteLine("No value found");

        Console.ReadLine();
    }

    static ValueWrapper<T> GetValue<T>(IDictionary<string, object> dictionary, string key)
    {
        ValueWrapper<T> t = new ValueWrapper<T>();

        object x = null;
        if (dictionary.TryGetValue(key, out x))
        {
            if (x.GetType() == typeof(T))
            {
                t.Value = (T)x;
                t.HasValue = true;
            }
        }

        return t;
    }
}

Thanks in advance!

Richard.

A: 

A few comments:

  1. t.Value = (T)x;

The cast is necessary. This is because t.Value is of type T and x is of type object. The strongly-typed nature of C# requires that you tell the compiler "look, I know this might unsafe but can you just try to do it for me anyway, either by conversion or unboxing or whatever? Thanks!"

2.

object x = null;
if (dictionary.TryGetValue(key, out x)) {
    if (x.GetType() == typeof(T)) {
        t.Value = (T)x;
        t.HasValue = true;
    }
}

return t;

What if x is an instance of a class that derives from T? Or if x is an instance of a class that implements an interface and T is that interface? Right now, you will return a instance of ValueWrapper<T> that indicates there was no object in the dictionary with the key key. I would argue this is very counterintuitive to what most people expect.

Additionally, if you're not going to throw up when dictionary does not contain a value matching the key key, I think you should rename your method to TryGetValue, accept a out parameter of type ValueWrapper<T>, and return a bool indicating success/failure.

3.

Responding to your comment, here's one solution.

public interface IValueWrapper {
    object Value { get; set; }
    bool HasValue { get; set; }
}

public class ValueWrapper<T> : IValueWrapper {
    public T Value { get; set; }
    object IValueWrapper.Value { 
        get { return Value; }
        set { this.Value = (T)value; }
    }
    public bool HasValue { get; set; }

    public ValueWrapper() {
        this.HasValue = false;
    }

    public ValueWrapper(T value) {
        this.Value = value;
        this.HasValue = value != null;
    }
}

public static class DictionaryExtensions {
    public static void Add<T>(
        this IDictionary<string, IValueWrapper> dictionary,
        string key,
        T value
    ) {
        ValueWrapper<T> valueWrapper = new ValueWrapper<T>(value);
        dictionary.Add(key, valueWrapper);
    }

    public static bool TryGetWrappedValue<T>(
        IDictionary<string, IValueWrapper> dictionary,
        string key,
        out ValueWrapper<T> value
    ) {
        IValueWrapper valueWrapper;
        if (dictionary.TryGetValue(key, out valueWrapper)) {
            value = (ValueWrapper<T>)valueWrapper;
            return true;
        }
        else {
            value = null;
            return false;
        }
    }
}

Usage:

var dict = new Dictionary<string, IValueWrapper>();
dict.Add("hello", 5);
ValueWrapper<int> value;
dict.TryGetWrappedValue("hello", out value);

You'll have to add parameter checking etc.

Jason
Thank you for replying so fast, I was seeing if there was a way that you could make it so object wasn't the V in the dictionary but maybe something else to prevent the unboxing as it can be quite slow.Also I have changed my method to ref a ValueWrapper and return true/false which does feel a bit better to me to be honest, I didn't think of that.
Richard Adnams
It should be `out` instead of `ref`.
Timwi
@Richard Adnams: I doubt it's leading to the performance problems but you can only know if you profile. Please see my edit for one way to solve the problem you are trying to solve, however.
Jason
Thank you Jason, you've been a big help.. now to get a coffee and digest this :)!
Richard Adnams