Why in the code below is it necessary to give the type when adding an item to the collection, if the generic type restriction explicitly states what type of T could be?

new InventoryItem() as T 

Code:

 using System; using System.Collections.ObjectModel; public class InventoryItem { public string Name { get; set; } } public class Inventory<T> where T : InventoryItem { private ObservableCollection<T> _inventory = new ObservableCollection<T>(); public Inventory() { _inventory.Add(new InventoryItem() as T); } } 

    2 answers 2

    Your code is strange and you should never write like that, because its absence is behind the type safety mask!

    See the heading of your class:

     public class Inventory<T> where T : InventoryItem 

    what is written here? And it says that a class can receive as a generic parameter of any successor of the InventoryItem !

    Those.:

     public class SuperInventoryItem : InventoryItem { } 

    and somewhere

     var invertory = new Inventory<SuperInventoryItem>(); 

    Guess what happens here? And better run and check!

    I hope you understand that if B is A , then the opposite is not true in the general case? This is what the compiler tells you, and therefore, in general, new A() as B will return null !

    So you have 2 ways:

    If your program does not foresee any successors of the InventoryItem , then you generally do not need to introduce such restrictions: T : InventoryItem , just work inside exclusively with the InventoryItem , and not T I would, for loyalty, then make the InventoryItem class also sealed .

    If you have InventoryItem descendants in your program and the class should be able to create them, then you should master the "Factory" pattern and transfer the Inventory factory to the InventoryItem designer. This will make your code cleaner: the Inventory class doesn’t have to know how any InventoryItem created and configured. .

    • Yes, casting from parent to heir looks weird when you notice him. In general, the example was written in a hurry and it seemed to me that it reflects the essence of the question well. In fact, Inventory has an AddItem method that accepts objects generated by the ItemFactory.Create () factory. I don’t know if it makes sense to pass it on to Inventory or was it an example in case objects are created in the constructor? In general, it seems to me that a good answer is an indication of the possibility to create an object with a heir of a generic type, as a result of which the type conversion will not work correctly. - Dmitry Malikov
    • Well, that you already decide, if you need to create inside, then transfer the factory, no - transfer the elements themselves. And in the constructor or where it doesn’t matter, you can also transfer an element to the constructor instead of creating it there inside. It is quite possible that InventoryItem cannot be created without any data that appears inside Invertory, then you can not do without transferring the factory to it - Andrey NOP
    • Okay, I thought up the rest of the evening yesterday, but I didn’t understand how transferring the object factory inside the Inventory class would help with type safety. The factory does not know anything about the generic type, so we can still initialize the generic to the derived class, and the factory will give us the parent classes. How to do it outside is understandable . But how to add items through the factory inside Inventory ? - Dmitry Malikov
    • one
      The factory should also be generalized: dotnetfiddle.net/G5nRog However, it is not entirely clear what problem you are solving, it may be better to ask a separate question about the architecture - Andrey NOP
    • Understood, abstract factory - what you need. By architecture, asking questions makes no sense, it is an intentional complication of the task to practice with generics. To the glory of the enterprise, so to speak. - Dmitry Malikov

    In your case, it simply does not know that T has a constructor without parameters (and how can he know?) [And he is, just implicit!]. So you have to bring manually.

    But if you say that T has a new() method, then there are no problems:

     using System; using System.Collections.ObjectModel; public class InventoryItem { public string Name { get; set; } } public class Inventory<T> where T : InventoryItem, new() // вооооот здееееесь { private ObservableCollection<T> _inventory = new ObservableCollection<T>(); public Inventory() { _inventory.Add(new T() ); } } 
    • True, for the above case it is. But I have a problem - in a real example, the InventoryItem has no constructor without parameters. Apparently, without casting is not enough. - Dmitry Malikov 4:26 pm
    • one
      Completely wrong answer, surprised why there is not a single minus. If T is an InventoryItem , then it is not at all necessary that the InventoryItem is T ! - Andrey NOP
    • one
      Well, that is information in the answer may look like a solution, but in fact does not relate to the essence of the question - Andrey NOP
    • Also reflection instead of an explicit constructor call ... Comrade, do not be so! - Kir_Antipov
    • one
      @DmitryMalikov, new T() deployed in Activator.CreateInstance() , although I would not say that this is a very clear reflection, but yes, this construction works an order or two slower than a direct constructor call: ru.stackoverflow.com/a/ 860921/218063 - Andrey NOP