Click here to Skip to main content
15,943,294 members
Please Sign up or sign in to vote.
5.00/5 (2 votes)
See more:
I have a little problem with Generics.
I think I already know the answer and if that is the case I am bummed out. But hopefully some of you can prove me wrong :)

I have some Generic Interfaces and some Generic Class Implementations.
The problem is that I need Attributes, and Attributes cannot handle Generics!
So I have no way of knowing what <T> is for my Interface and Classes. Instead I have my Interface<T> derive from a non-Generic Interface... Unfortunately the non-Genericness is slithering through my Class structure like a non-Generic snake!
I have already made good working Classes that use my Generic Interfaces and they are usable also without the Attributes. So dropping Generics alltogether is no option.
Is there a way however to convert a BaseInterface to a DerivedInterface<T>? Is this also possible when you only know T at runtime?
Something like DerivedInterface<myVar.GetType()>?

My question may be a bit cryptic in the late evening, so if I am not clear enough do not hesitate to ask :)
Thanks!

Edit after next edit:
After really close re-re-re-re-consideration of my code I have concluded that my design is busted (yes, even I make mistakes ;p )! I do need not to cast from IDerived<int> to IDerived<Object>. However I would still like to know if it is possible.

Edit:
Thanks to SAKryukov for a very detailed answer! It really helped! :)
After having a good night sleep and reading SAKryukovs answer I can now put my question into better words than last night though. So here it goes again.
In the following code snippet, which closely matches my own scenario, I must convert a Something<int> to a Something<Object>. The way I see it is that int is an Object so this conversion should be no problem. Wrong!
As SAKryukov put it (and he should correct me if I am wrong) it is because Something<int> is the type of Something<int> (and not Something and then an int or a Generic Something). And even though int Inherits from Object Something<int> does not Inherit from Something<Object>.
C#
class Program
{
	static void Main(string[] args)
	{
		DerivedSomething thing = new DerivedSomething();
		if (CheckMustStop<int>(thing))
		{
			Console.WriteLine("Stop!");
		}
		else { Console.WriteLine("Don't stop!"); }
		Console.ReadKey();
	}

	static bool CheckMustStop<t>(Something<t> thing)
	{
		// This is where stuff gets nasty!
		// An int is an Object, but casting will not work!
                // Mind you that I know T is an int. In my own code I do not know this.
		Something<Object> tempThing = (Something<Object>)thing;
		return tempThing.MustStop;
	}
}

public class DerivedSomething : Something<int>
{
	public void DoSomething(int item)
	{
		// Do stuff here, <t> is important for item.
	}
		public bool MustStop
	{
		// <t> does not matter!
		get { return true; }
	}
}

public interface Something<t>
{
	void DoSomething(T item);
	bool MustStop { get; }
}

As you see it does not matter which type T is in the example. Because no matter which type T is it will always have a MustStop Property which does not care about the type of T. This leads me to believe that Something<T> actually needs a non-Generic base Interface to be able to check if Something<T> of whatever type must stop.
Such as this:
C#
public interface Something<t> : Something
{
	void DoSomething(T item);
}

// I can always cast to Something without knowing the type of T!
public interface Something
{
	void DoSomething(Object item);
	bool MustStop { get; }
}

This creates new problems, such as Something only being usable on its own when it has a non-Generic DoSomething Method and the fact that I need a non-Generic base Interface for almost all my Generic Interfaces. Which really tends to clutter up code :)

I hope this further clarifies my problem. Any help is greatly appreciated :)
Posted
Updated 15-Oct-11 23:50pm
v5
Comments
BillWoodruff 15-Oct-11 21:09pm    
I ... kind of ... get what you are asking here, and it's a fascinating question (which I just hi-fived), one I'd like to know the answer to as well. What would help me, in this case, would be a code example. thanks, Bill
Sergey Alexandrovich Kryukov 15-Oct-11 21:55pm    
Imagine that -- I've even given pretty comprehensive answer. Please see, hope it's interesting.
I'll also vote 5 for interesting question.
--SA
Sander Rossel 16-Oct-11 4:48am    
Thanks for the hi-five! As a response to SAKryukovs fine answer I have actually edited my question in a way that you might too find interesting :)
Sergey Alexandrovich Kryukov 15-Oct-11 21:56pm    
My 5 for this interesting question, even though it is not 100% clear and not 100% correct. :-)
Hope my answer will give some ideas.
--SA
Sander Rossel 16-Oct-11 4:46am    
Thanks for your once again great answer! It did give me some ideas as you can see in my edited question :)

If the purpose of the casting is to use members of the derived class, you can use Type.MakeGenericType[^] method, in order to get the actual generic type and, invoke the wanted methods using reflection[^].
 
Share this answer
 
v3
Comments
BillWoodruff 15-Oct-11 21:15pm    
Hi-five for this: absolutely fascinating. I hope one day to be able to understand such esoteric use of generics, but, today, I am very far from that goal:) !
Shmuel Zang 16-Oct-11 16:53pm    
Thanks.
Sergey Alexandrovich Kryukov 15-Oct-11 22:04pm    
Very good idea, even though it does not really answer the question (which is about a cast, which is different).
Anyway, such things is very good to know, I voted 5, but -- please see my answer.
--SA
Shmuel Zang 16-Oct-11 17:19pm    
Thanks.
Sander Rossel 16-Oct-11 3:54am    
Thanks! I didn't know that one. May actually come in handy so my 5 :)
Very interesting question, even though it is not quite correct.

The answer is: of course you can cast from non-generic to generic. But…
Let's try to sort things out.

I will try to differentiate some things which look subtle and usually can be ignored, but is the exotic case you're trying to make are important.

First of all, BaseInterface to a DerivedInterface<T> are types, and you never convert types. Think about it: you always convert objects.

Next step. Type casting in the sense you are talking about is never a conversion. It does not move any data anywhere. It just allows to treat the value of the variable declared as one type as another type. One particular case is down-casting of the variable of the types related as base/derived:

C#
class Base {/*...*/}
class Derived : Base {/*...*/}
Base b = new Derived(/*...*/);
Derived d = (Derived)b;


It does not matter, classes or interfaces. A class or struct is only needed to have an instance to demonstrate the case. (Important! Note that structures can also implement interfaces — not very well known fact.) Note that this cast is only possible because b has a run-time type which is actually Derived. You can only cast a variable of some compile-time type to the actual run-time type or one of its bases.

Finally, realize that. Even though generic type do exist not only during compile time but also during run-time, which can be seen via Reflection, generic types do not exist in objects. By their nature, all objects are always of some fully-instantiated type. You can have an object of System.Collections.Generic.List<string>, never of the generic List type. In makes the problem pretty simple:

C#
interface IBase {
    void BaseOperation();
    //...
} //interface IBase

interface IDerived<OPERAND> : IBase {
    OPERAND Sum(params OPERAND[] members);
    OPERAND Product(params OPERAND[] members);
    //...
} //interface IDerived

abstract class Arithmetic<OPERAND> : IDerived<OPERAND> {
    void IBase.BaseOperation() { }
    OPERAND IDerived<OPERAND>.Sum(params OPERAND[] members) { return ImplementSum(members); }
    OPERAND IDerived<OPERAND>.Product(params OPERAND[] members) { return ImplementProduct(members); }
    protected internal abstract OPERAND ImplementSum(params OPERAND[] members);
    protected internal abstract OPERAND ImplementProduct(params OPERAND[] members);
    protected internal OPERAND SomeAuxMethod() { return default(OPERAND); }
    //...
} //class Arithmetic

class IntegerArithmetic : Arithmetic<int>, IDerived<int> {
    protected internal override int ImplementSum(params int[] members) {
        int result = 0;
        foreach (int member in members) result += member;
        return result;
    }
    protected internal override int ImplementProduct(params int[] members) {
        int result = 0;
        foreach (int member in members) result *= member;
        return result;
    }
    //...
} //class IntegerArithmetic

class Test {
    void TestMethod() {
        IBase @object = new IntegerArithmetic();
        //...
        IDerived<int> integerArithmeticObject =
            (IDerived<int>)@object;
        int sum = integerArithmeticObject.Sum(new int[] {1, 12, 113, });
        //will work!
    }
} //class Test


The main benefit of the method of abstraction shown above is having implementation of some algorithms not depending on concrete type of OPERAND in abstract class Arithmetic represented by the interface IDerived. Those methods are not shown; they can include working with containers of OPERANDS, search, sorting (will need generic constraint to allow comparison operators) and a lot more. Numeric-specific operations a postponed to the classes instantiating generic types. This problems is particularly difficult with numeric types and is much easier when generic parameters are constraint to be classes or (even easier) interfaces.

As I can understand, you have "generic" problems with designing your code. Well, I understand, this is natural, even though I don't really understand your problem. The understanding probably requires some sample code. However, I hope that better understanding of combination of generics and OOP could help you to have a fresh look at your problem and improve things. I also hope my simple sample code can give you some ideas.

Good luck,
—SA
 
Share this answer
 
v7
Comments
BillWoodruff 15-Oct-11 22:07pm    
Hi-Five for this ! I can 'follow' this pretty well; I think the 'sticking point' for me, in this 'knowledge domain,' is connecting these 'powerful ideas' to a sense of: in what scenarios they are particularly useful and appropriate. thanks, Bill
Sergey Alexandrovich Kryukov 15-Oct-11 22:49pm    
Thank you, Bill.

You probably mean combination of OOP and generics... I've used and created such scenarios and can say they can be pretty complex, especially when you design such code, not when you use it. In one word, they are important when you develop a library without knowing all possible uses of it, so you want to make it very flexible... Giving just one sample will hardly help to feel such situations. It would need a really big article.
--SA
Mehdi Gholam 16-Oct-11 1:05am    
My 5!
Sergey Alexandrovich Kryukov 16-Oct-11 2:32am    
Thank you, Mehdi.
--SA
Sander Rossel 16-Oct-11 4:45am    
Thanks for a very good and detailed answer! My 5.
This answers part of my question and sets me on the right track. It also helped me to see my real problem.
Which is actually not casting Derived<t> to Base or vice versa, but to cast Derived<derived> to a Derived<Base>. See my edited question for details. Any help is greatly appreciated.
If you want to answer please create a new answer (instead of editing this one). This helps me to keep things apart and to vote the other 5 you deserve :)
Or in my case, <t> does not matter. I just want to know if a certain Object is of type SomeClass<anytype>.
Because the case is that I need to check a non-Generic Property on a Generic Interface.
As requested, I'm answering an edited version of the question is a separate answer. This question is indeed radically different from the original question. :-)

All the problem is the misuse of the non-generic method of generic interface. Here is how a fixed version of the code might look (and it will work, of course):

C#
public class DerivedSomething : ISomething<int> {
    public void DoSomething(int item) {
        // Do stuff here, <T> is important for item.
    } //DoSomething
    public bool MustStop {
        // <T> does not matter!
        get { return true; }
    } //MustStop
} //class DerivedSomething

public interface ISomething<T> {
    void DoSomething(T item);
    bool MustStop { get; }
} //ISomething<T>

class Test {
    void TestMethod() {
        DerivedSomething thing = new DerivedSomething();
        if (thing.MustStop)
            Console.WriteLine("Stop!");
        else
            Console.WriteLine("Don't stop!");
    } //TestMethod
} //class Test


I also renamed Something to ISomething to meet (good) Microsoft naming conventions for interfaces. There is certain confusion due to the fact you miss one important step which partially defeats the purpose of generic interfaces. To make the inheritance chain really practical, you need to make is like this: generic interface => generic (possibly abstract) class implementing methods agnostic to generic parameters => set of complete implementation classes — using the idea shown in my previous answer. The term "class" could be replaces with "struct".

[EDIT #1]

This is amazing: I just made a real "Freudian slip" (http://en.wikipedia.org/wiki/Freudian_slip[^])!

In the above paragraph, I wanted to type "(good) Microsoft naming conventions", but typed "goof Microsoft naming conventions"!

No, they are really good, not goof; and I'm using them.

[END EDIT #1]

[EDIT #2]

After some thinking, I realized that I can see two more problems in the code above. I explained above, that 1) missing intermediate implementation class implementing just non-generic part of the interface; in this way you would not repeat implementation of those non-generic methods/properties in each of generic type-specific implementation. Two remaining problems are: 2) interfaces are designed to be use during run-time; remove all interfaces from your code — it will work in exact same way, why writing them? you should really use your classed via interfaces; 3) you use implicit interface implementation (via "public"); but explicit interface implementation is much clearer.

My fix of the problems (2) and (3) required change in usage but then the use of interfaces is really justified in a practically beneficial way, and lack of code reuse if fixed (the schema you want is useful only when you need to have more than one class like DerivedSomething, but if you try to write more classes you will see that MustStop implementation is not reused); so, the code fixing all together will look like this:

C#
// interfaces:

    public interface IAgnostic {
        bool MustStop { get; }
    } //interface IAgnostic

    public interface ISomething<T> : IAgnostic {
        void DoSomething(T item);
    } //ISomething<T>

//implementations:
 
    abstract class Agnostic : IAgnostic {
        bool IAgnostic.MustStop {
            get { return true; }
        } //MustStop
    } //class Agnostic

    class DerivedSomething : Agnostic, IAgnostic, ISomething<int> {
        void ISomething<int>.DoSomething(int item) {
            // Do stuff here, <T> is important for item.
        } //DoSomething
        //implementation of MustStop is reused
    } //class DerivedSomething
    class DerivedSomethingElse : Agnostic, IAgnostic, ISomething<int> {
        void ISomething<int>.DoSomething(int item) {
            // Do stuff here, <T> is important for item.
        } //DoSomethingElse
        //implementation of MustStop is again reused
    } //class DerivedSomething

// usage:

    class Test {
        void TestMethod() {
            DerivedSomething thing = new DerivedSomething();
            //an extra variable somethigReferenced helps to
            //avoid type case, which would be less safe:
            ISomething<int> somethigReferenced = thing; 
            if (somethigReferenced.MustStop)
                Console.WriteLine("Stop!");
            else
                Console.WriteLine("Don't stop!");
        } //TestMethod
    } //class Test


I fix to the lack of code reuse is done via additional interface IAgnostic that is, agnostic to generic parameters and additional abstract implementation class IAgnostic. The fix is demonstrated by having two different terminal completely implementing classes, DerivedSomething and DerivedSomethingElse.

Now it's really right.

[END EDIT #2]

Now, I gave you the resolution which does not even touch the root topic collectively named "covariance and contravariance". To really understand these issues you really need to learn this topic. In C# practice, it's mostly about new features introduced to C# v.4 in comparison with prior versions.

So, try to read and understand this:
http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29[^],
http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-contravariance-in-c-part-one.aspx[^] (you really need to read all parts of it, but it needs some effort to find them; the navigation in this blog really needs improvement!),
http://blogs.msdn.com/b/ericlippert/archive/2007/10/26/covariance-and-contravariance-in-c-part-five-interface-variance.aspx[^],
http://blogs.msdn.com/b/ericlippert/archive/2009/11/30/what-s-the-difference-between-covariance-and-assignment-compatibility.aspx[^].

Just to give you the idea how much C# and .NET are lost in covariance/contravariance issues:
C#
//you can do this:
string[] strings = new string[] { "1", "2", };
object[] objectsobjects = strings;

//but you cannot do this, no matter with v.4 or prior version:
System.Collections.Generic.List<string> strList = new System.Collections.Generic.List<string>();
System.Collections.Generic.List<object> objList = strList; //with case or not


The covariance/contravariance issues come into play each time there is a class using instances of other classes when using and used types are derivable. So, the issues are about arrays, generic containers and generic delegates. This goes beyond the casting topics we're discussing here.

—SA
 
Share this answer
 
v10
Comments
Espen Harlinn 16-Oct-11 17:32pm    
Excellent effort, Sergey - obviously this deserves a 10 on it's own, but as that's not possible you have my 5+ :)
Sergey Alexandrovich Kryukov 16-Oct-11 17:38pm    
Thank you very much, Espen.
(I just added a paragraph and then a fix.)
--SA
Sander Rossel 16-Oct-11 17:38pm    
Well, that just really did it. Thanks for the links, I am going to read them all. As Espen said, that deserves a 10, but I can only vote 5. So here it is once again!
I have already removed half of the Interfaces from my code (all non-generic ones) since they were simply not necessary anymore after your explanation.
Thanks for your efforts, it is really appreciated! :)
Sergey Alexandrovich Kryukov 16-Oct-11 17:43pm    
You're very welcome, thank you for interesting question.
By the way, I just added one more funny paragraph, see [EDIT].

Good luck, call again.
--SA
Sergey Alexandrovich Kryukov 16-Oct-11 20:46pm    
Actually, its wasn't all. After some thinking, I paid attention to more problems which I've fixed in new update, see [EDIT #2]. Now you should have right picture.
--SA

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900