1

I'm looking for a way to treat ALL .Net datatypes consistently so I can create the pattern below where any type implementing IGetValue<out T> will cast to IGetValue<object>. For some reason, if T is a struct, it doesn't work and I don't understand why. Is there a way I can implement the following pattern??

public interface IGetValue<out T>
{
    T Value
    {
        get;
    }
}

public class GetValue<T> : IGetValue<T>
{
    public GetValue(T value)
    {
        _value = value;
    }

    private T _value;
    public T Value
    {
        get { return _value; }
    }
}


class Program
{
    static void Main(string[] args)
    {
        IGetValue<string> GetString = new GetValue<string>("Hello");
        IGetValue<int> GetInt = new GetValue<int>(21);

        //This works!!!
        if (GetString is IGetValue<object>)
        {
            Console.WriteLine("GetValue<string> is an IGetValue<object>");
        }
        else
        {
            Console.WriteLine("GetValue<string> is not an IGetValue<object>");
        }

        //This doesn't work!!! Why????
        if (GetInt is IGetValue<object>)
        {
            Console.WriteLine("GetValue<int> is an IGetValue<object>");
        }
        else
        {
            Console.WriteLine("GetValue<int> is not an IGetValue<object>");
        }

        Console.ReadKey();
    }
}

Edit:

I realize what I'm trying to accomplish here seems vague, but this is part of a larger design whose explanation would be too verbose. What I need is to have all of my IGetValue<T>s to share a common type or interface with a property named "Value" that returns an object. Why is the verbose part.

Verax
  • 2,409
  • 5
  • 27
  • 42

2 Answers2

8

It doesn't work because generic variance doesn't apply to value types... they have different representations, whereas variance with reference types can happen without the CLR having to perform any conversions.

So for example, you can treat an IEnumerable<string> as an IEnumerable<object>, but you can't treat an IEnumerable<int> as an IEnumerable<object>.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Ok, thank you for the explanation, but is there a way to implement the pattern above. – Verax Apr 16 '11 at 10:22
  • 1
    @Verax: Not as written, no. A `Foo` will never be a `Foo`. It's hard to tell exactly what you're trying to do though. – Jon Skeet Apr 16 '11 at 10:52
  • I'm sorry for the vagueness. This is a part of a larger design (as you are probably aware) and I would have to explain the larger design for anyone to fully understand my goal. But that would be too verbose for this forum. Please just answer me this. `int` inherits from `object` (eventually), yes? `object` is a reference type, yes? So why does `int` need to be boxed to be a reference type if it inherits from a reference type in the first place? – Verax Apr 16 '11 at 11:23
  • "int inherits from object (eventually), yes?". Well yes and no. In your context I'd say more no than yes. Look here Jon Skeet's answer http://stackoverflow.com/questions/1793357/do-value-types-integer-decimal-boolean-etc-inherit-from-object – pero Apr 16 '11 at 12:02
  • @Verax: Because it's *not* a reference type. How much do you understand about the difference between value types and reference types? I suggest you look into that before trying to understand the complexities of generic variance etc. – Jon Skeet Apr 16 '11 at 18:49
  • I understand the difference between value and reference types. What I don't understand is how Microsoft has been able to make a value type (e.g. `int`) inherit from a reference type (e.g. `object`) and have it not be a reference type. It's inheriting from a reference type, is it not? So, why is it not a reference type? I know it is a value type, but how did Microsoft inherit from `object` and magically remove all reference type semantics? Is it a value or a reference type under the hood? – Verax Apr 17 '11 at 03:37
  • I believe Marc Gravell's answer here explains it. It must be a cheat. [http://stackoverflow.com/questions/1205444/value-type-vs-reference-type-object-class-c](http://stackoverflow.com/questions/1205444/value-type-vs-reference-type-object-class-c) – Verax Apr 17 '11 at 03:58
  • @Verax: Inheriting the members isn't the same as inheriting being a reference type, basically. I'm sure if you search around you'll find a lot more about boxing. – Jon Skeet Apr 17 '11 at 06:11
0

I ended up solving my immediate need by creating a non-generic IGetValue interface and implementing it explicitly in the class. Here's the solution:

public interface IGetValue
{
    object Value
    {
        get;
    }
}

public interface IGetValue<out T>
{
    T Value
    {
        get;
    }
}

public class GetValue<T> : IGetValue<T>, IGetValue
{
    public GetValue(T value)
    {
        _value = value;
    }

    private T _value;
    public T Value
    {
        get { return _value; }
    }

    object IGetValue.Value
    {
        get { return _value; }
    }
}


class Program
{
    static void Main(string[] args)
    {
        IGetValue<string> GetString = new GetValue<string>("Hello");
        IGetValue<int> GetInt = new GetValue<int>(21);

        if (GetString is IGetValue)
        {
            Console.WriteLine("GetValue<string> is an IGetValue");
        }
        else
        {
            Console.WriteLine("GetValue<string> is not an IGetValue");
        }


        if (GetInt is IGetValue)
        {
            Console.WriteLine("GetValue<int> is an IGetValue");
        }
        else
        {
            Console.WriteLine("GetValue<int> is not an IGetValue");
        }

        Console.ReadKey();
    }
}
Verax
  • 2,409
  • 5
  • 27
  • 42