tags:

views:

118

answers:

4

Hello,

What I basically wish to do is design a generic interface that, when implemented, results in a class that can behave exactly like T, except that it has some additional functionality. Here is an example of what I'm talking about:

public interface ICoolInterface<T>
{
    T Value { get; set; }
    T DoSomethingCool();
}

public class CoolInt : ICoolInterface<int>
{
    private int _value;

    public CoolInt(int value)
    {
        _value = value;
    }

    public int Value
    {
        get { return _value; }
        set { _value = value; }
    }

    public int DoSomethingCool()
    {
        return _value * _value;
        // Ok, so that wasn't THAT cool
    }
}

And this is all well and good, but in order to use CoolInt, I need to do something like this:

CoolInt myCoolInt = new CoolInt(5);
int myInt = myCoolInt.Value;

I'd much rather, in terms of assignment at least, that CoolInt works just like int. In other words:

CoolInt myCoolInt = 5;
int myInt = myCoolInt;

To achieve this, I added these two conversion operators to my CoolInt class:

    public static implicit operator CoolInt(int val)
    {
        return new CoolInt(val);
    }

    public static implicit operator int(CoolInt obj)
    {
        return obj.Value;
    }

Works awesomely. Now, I would prefer it if I could add these two overloads to the interface, so that implementers of the interface are forced to implement these operators. The problem is, the prototypes of these operators refer directly to CoolInt.

C# has a lot of "placeholder" names for things that are implicitly defined or have yet to be defined. The T that is conventionally used in generic programming is one example. I suppose the value keyword, used in Properties, is another. The "this" reference could be considered another. I am hoping that there's another symbol I can use in my interface to denote "the type of the class that is implementing this interface", e.g. "implementer".

    public static implicit operator implementer(int val)
    {
        return new IntVal(val);
    }

    public static implicit operator int(implementer obj)
    {
        return obj.Value;
    }

Is this possible?

+4  A: 

Why don't you create an abstract class? This way you can build some "default" functionality into your class.

TheCloudlessSky
I think that gets me part of the way there, but I think I would still run into a problem. Let's say I have AbstractCoolClass, and the concrete version is called CoolInt, as before.Well, the implicit conversion operator from int to CoolInt would return an AbstractCoolClass reference, which would then have to be explicitly casted to a CoolInt.I'd rather do without explicit casts, but it looks like I may have no choice!
Knarf Navillus
An abstract *generic* class wouldn't need to return an `AbstractCoolClass` object it could return a `T`.
Hightechrider
I need to do the conversion both ways. So, I need an operator that accepts a `T` and returns an instance of my class, and another method that accepts an instance of my class and returns a `T`.In both cases, I would need to use `AbstractCoolClass` as the data type for the instance of my class. In the latter case, it's not a problem, because I can simply have the overloaded method cast from `AbstractCoolClass` to the concrete class (e.g., `CoolInt`).However, in the former case, I have to return an `AbstractCoolClass` and it's up to the client developer to do the cast.
Knarf Navillus
+2  A: 

Sadly no :(

C# doesn't do well when it comes to operator overloading (This is one example, another is generic constraints on certain operator types).

cwap
@cwap, yes, it's odd how operator overloading sometimes doesn't work: in the abstract generic example I posted here it appears to work one way but not the other!
Hightechrider
A: 

Why not use extension methods instead? That lets you "add" methods to int without having to use a different type.

Dan Puzey
A: 

This is probably the closest you can get using an abstract base type, but sadly even this has an issue with one of the implicit operators and you have to do:-

        CoolInt x = (CoolInt)5;
        int j = x;

Close enough?

    // Slightly sneaky, we pass both the wrapped class and the wrapping class as type parameters to the generic class
    // allowing it to create instances of either as necessary.

    public abstract class CoolClass<T, U>
        where U : CoolClass<T, U>, new()
    {
        public T Value { get; private set; }

        public abstract T DoSomethingCool();

        // Non-public constructor
        protected CoolClass()
        {
        }

        public CoolClass(T value)
        {
            Value = value;
        }

        public static implicit operator CoolClass<T, U>(T val)
        {
            return new U() { Value = val};
        }

        public static implicit operator T(CoolClass<T, U> obj)
        {
            return obj.Value;
        }

    }

    public class CoolInt : CoolClass<int, CoolInt>
    {
        public CoolInt()
        { 
        }

        public CoolInt(int val)
            : base(val)
        {
        }

        public override int DoSomethingCool()
        {
            return this.Value * this.Value; // Ok, so that wasn't THAT cool
        }
    }
Hightechrider