Home > other >  How to create a type that Is just a variable containing another type?
How to create a type that Is just a variable containing another type?

Time:11-24

Sorry for my verry badly written title, I'm a beginner programmer and I just started on a c# winforms app. In one function I create an object of some type and then in other functions I iterate through a list of that type of objects, however I'm switching the type of control I'm using and when I do, I have to change the type declaration of my object in over twenty places. Is there a way to create a variable that holds that type and than define all my objects off that variable so I only have to specify the type once and then change that variable. Because I'm using winforms controls as my class types all the functions I call are all then same no matter what type my objects are, so all I need to do is change the type declaration and that's it, sorry if this is a stupid question and any help would be appreciated.

Here is a snippet of my code for context:

private void function1(object sender, EventArgs e) //not my actual function because the real function has lots of other unrelated code
{
    ListView PlaceType = new ListView(); // these ListView types i would like to replace with a placeholder if possible
    ListView listview = new ListView();
    int count2 = autolayoutGroups.Controls.OfType<ListView>().ToList().Count();
    listview.Size = new System.Drawing.Size(150, 100);
    listview.BackColor = normalColor;
    listview.BorderStyle = BorderStyle.Fixed3D;
    listview.ForeColor = System.Drawing.Color.Black;
    listview.Name = "Group"   count2;
    listview.MouseDown  = Select;
}
private void function2(object sender, EventArgs e)
{
   List<ListView> list_of_groups = autolayoutGroups.Controls.OfType<ListView>().ToList(); // a place where I need some type of placeholder

   foreach (ListView l in list_of_groups)
   {
        // do something here
   }
}
private void function3(object sender, EventArgs e) // I have several functions like this 
//and if I change the control I'm using I have to change the types in every function
{
   List<ListView> list_of_groups = autolayoutGroups.Controls.OfType<ListView>().ToList(); // a place where I need some type of placeholder

   foreach (ListView l in list_of_groups)
   {
        // do something here
   }
}

CodePudding user response:

If I understand your problem correctly, you currently have some code where you use a ListView, and you want the same code, but instead of ListView you want some other class, for instance a DataGridView, or a ComboBox. Of course this other class must also have the methods that you used on the ListView.

In C# this concept is called a generic. You have generic classes and generic methods.

You define the generic by typing an identifier instead of the part that you want to replace with another type.

In your case: you want to replace ListView by DataGridView. In function1 you create a ListView, and set some properties. First we'll put this creation in a separate method, you will have something like this:

private ListView CreateListView()
{
    ListView listview = new ListView();
    int count2 = autolayoutGroups.Controls.OfType<ListView>().Count();
    listview.Size = new System.Drawing.Size(150, 100);
    listview.BackColor = normalColor;
    listview.BorderStyle = BorderStyle.Fixed3D;
    listview.ForeColor = System.Drawing.Color.Black;
    listview.Name = "Group"   count2;
    listview.MouseDown  = Select;
    return listView;
}

private void function1(object sender, EventArgs e)
{
    ListView createdListView = this.CreateListView();
    // TODO: do something with the created ListView
}

(Small optimization, out of scope of the question): to calculate count2, don't create a List of all ListViews, and then Count them; use Count() on the IEnumerable<ListView>.

To change CreateListView such that it can create anything of type TMyType, define the generic method like this:

private TMyType Create<TMyType>()
{
    TMyType createdObject = new TMyType();
    int count2 = autolayoutGroups.Controls
                                 .OfType<TMyType>()
                                 .Count();
    createdObject.Size = new System.Drawing.Size(150, 100);
    createdObject.BackColor = normalColor;
    createdObject.BorderStyle = BorderStyle.Fixed3D;
    createdObject.ForeColor = System.Drawing.Color.Black;
    createdObject.Name = "Group"   count2;
    createdObject.MouseDown  = Select;
    return createdObject ;
}

So all I did was, that whenever I save ListView, I replaced it with TMyType, the type that should be created.

Usage:

ListView createdListView = this.Create<ListView>();
DataGridView createdDataGridView = this.Create<DataGridView>();
ComboBox createdComboBox = this.Create<ComboBox>();

There is only one problem. You'll have to tell the compiler that TMyType has a default constructor (you want to do new TMyControl()), and that is has methods like Size, BackColor, ForeColor, etc.

If TMyType would be a class derived from Control, then you would be certain that it has the desired constructor and knows all methods that you need to use.

To say that a generic type is derived from a certain type, you use the following structure:

private TMyType Create<TMyType>() where TMyType: Control
{
    // because you are certain the TMyType is derived from Control
    // you can use all methods of class Control  
}

This answers your question: create a generic method

Some other things about generics

Another example: If you want to inform the compiler that the generic type implements IComparable:

private T Process<T>(T input) where T: IComparable {...}

Or multiple:

private T Process<T>(T input) where T: IComparable, IEquatable {...}

And finally if you want to require that the generic type has a default constructor:

private T Process<T> () where T: new

CodePudding user response:

It's not really clear what the goal is but I suppose you can make a property that returns your commonly used list:

private List<ListView> AutolayoutGroupControls => 
  autolayoutGroups.Controls.OfType<ListView>().ToList()

Then you can

foreach(var lv in AutolayoutGroupControls)}
  ...
}

But it doesn't offer much; if you change that prop to return something else you still have a stack of changes to make. If your loops always do the same thing put it into a method and call it from N event handlers

  • Related