1

in my Silverlight 4 application, I am trying to use generics, but there is another problem, that I hope to solve with the help of the stackoverflow-community:

I have a Tree-Structure, that can have two modes, Editor-mode, where the tree is created, nodes added, moved, deleted etc. and a Configurator-mode, where the user can i.e. select nodes of the tree.

To represent the tree, I created a base class for both modes, and a derived class for each mode. As the Editor-mode can only have Editor-Nodes and the Configurator-Mode can only have Configurator-Nodes, I made the baseclass generic:

public abstract class ServiceModelBase<TRootNodeType>
    where TRootNodeType : ServiceNodeVMBase
{
  public TRootNodeType RootNode
  {
    get { return _rootNode; }
  }
  ...
}

public class ServiceModelConfigurator : ServiceModelBase<ServiceNodeVMConfigurator>
public class ServiceModelEditor : ServiceModelBase<ServiceNodeVMEditor>

Both ServiceNodeVMConfigurator and ServiceNodeVMEditor inheriting from ServiceNodeVMBase

The application can save and load the saved data. Loading works (in short) this way:
1.) Deserialize the serialized data in a special Datatransferobject.
2.) Depending on the type of the Datatransferobject, creating a ServiceModelConfigurator or ServiceModelEditor
3.) Firing an event, that contains (beside others) the created ServiceModel

I have created a class derived from EventArgs that have to store the ServiceModel. As this ServiceModel can be Editor or Creator, I declared the Property to store it of the baseclasstype:

public class ServiceModelLoadedEventArgs : EventArgs
{
  public ServiceModelBase<ServiceNodeVMBase> ServiceModel;
  ...
}

But unfortunatly, I cannot assign the derived ServiceModelEditor/Configurator to the EventArgs ServiceModel variable:

ServiceModelLoadedEventArgs args = new ServiceModelLoadedEventArgs();
args.ServiceModel = new ServiceModelEditor();

The compiler tells me, that it cannot convert ServiceModelEditor in ServiceModelBase

Can anyone tell me, how I have to write the code for the EventArgs-class that I can assign a ServiceModelEditor or ServiceModelConfigurator to the ServiceModel variable?

PS: I want to apologize that this is just another generics related question from me, but I fear generics and me are not really friends yet.

Aaginor
  • 4,516
  • 11
  • 51
  • 75
  • 1
    It would really help if you followed .NET naming conventions and gave type parameters names beginning with T. Without careful examination, `RootNodeType` looks like a normal type name, not a type parameter name. – Jon Skeet Dec 01 '11 at 15:43
  • Sorry about that! I didn't know. I have edited the type parameter to start with T now. – Aaginor Dec 01 '11 at 16:01

2 Answers2

1

To use covariance you'll have to declare a covariant interface:

public interface IServiceModelBase<out TRootNode> 
     where TRootNode : ServiceNodeVMBase 
{ 
    TRootNode RootNode { get; }     
} 

public abstract class ServiceModelBase<TRootNode> : IServiceModelBase<TRootNode>
{
    ...
}

public class ServiceModelLoadedEventArgs : EventArgs   
{   
  public IServiceModelBase<ServiceNodeVMBase> ServiceModel { get; set; }  
  ...   
}   

public class ServiceModelEditor : ServiceModelBase<ServiceNodeVMEditor>  

And:

ServiceModelLoadedEventArgs args = new ServiceModelLoadedEventArgs();        
args.ServiceModel = new ServiceModelEditor();
phoog
  • 42,068
  • 6
  • 79
  • 117
  • Yep, this works, many thanks! I have read about covariance (and contravariance) yesterday. What is unclear to me: Why do you need to use an interface (or delegate) and cannot define the co(ntra)variance in a "normal" class like Public class MyClass ... – Aaginor Dec 02 '11 at 08:29
  • @Aaginor Eric Lippert's answer to this question http://stackoverflow.com/q/2733346/385844 (Why isn't there generic variance for classes in C# 4.0?) discusses that. It looks like implementing it would be a big headache, and you don't get much more benefit than you would by just implementing a covariant interface on the class. – phoog Dec 02 '11 at 18:28
  • Ah, thanks for the link! I think I basically understand, what the problem would be: Unable to set the generic type. This also means, that I could not define a setter for RootNode in the interface, right? – Aaginor Dec 06 '11 at 15:11
0

You can use co-variance:

public abstract class ServiceModelBase<out RootNodeType>
     where RootNodeType : ServiceNodeVMBase
{


}
Yochai Timmer
  • 48,127
  • 24
  • 147
  • 185
  • 1
    out can only be used with interfaces and delegates (this is what VS tells me). I'm currently reading about covariance and contravariance and as far as I understand it, I need to create an interface for my ServiceModelBase class. – Aaginor Dec 01 '11 at 16:05