I have a general question about the structure of my object model. Perhaps I am approaching this with tunnel vision from the wrong direction. I have two classes, Item and SerializedItem. I have defined them as such:
public class Item
{
public string ItemNumber { get; set; }
public string Description { get; set; }
public double Cost { get; set; }
}
public class SerializedItem : Item
{
public string SerialNumber { get; set; }
public MyObject Location { get; set; }
}
An Item is a generic definition of an item, and contains information common to that product. SerializedItem is a representation of a specific, physical item. My difficulty lies in the fact that only one Item with a particular ItemNumber should exist in memory at anytime, and I am not sure the best pattern to use to enforce that constraint while allowing a SerializedItem to act as its base type.
Maybe this is a more appropriate approach? I don't have a lot of experience using the 'New' keyword, and I've shied away from using it in the past in favor of an inheritance structure that didn't require its use.
public class Item
{
public string ItemNumber { get; set; }
public string Description { get; set; }
public double Cost { get; set; }
}
public class SerializedItem : Item
{
private Items _item;
public SerializedItemz(Item item)
{
_item = item;
}
public new string ItemNumber
{
get { return _item.ItemNumber; }
set { _item.ItemNumber = value; }
}
public new string Description
{
get { return _item.Description; }
set { _item.Description = value; }
}
public new double Cost
{
get { return _item.Cost; }
set { _item.Cost = value; }
}
public string SerialNumber { get; set; }
}
I would appreciate any guidance on how to approach this. I'm not tied to any particular solution.
To provide some clarity:
The Item class is a representation of a particular product, 'Widget A.' It has information about the Widget A's cost, weight, dimensions, etc. No matter how many Widget As are produced, they all share this information.
The SerializedItem class is a representation of an actual item in that product line, 'Widget A 001.' It contains information about the physical location of that item and it's production and sales history.
If the Item object is updated, all SerializedItems should reflect that change.
I am not sure the best pattern to use to enforce that constraint while allowing a SerializedItem to act as its base type
At first glance a flyweight factory pattern would seem appropriate. Create a class whose responsibility is to create Items, keep track of which ones have already been created, and ensure that only one item with a given key is created.
You can also build logic into the factory to create different subtypes like SerializedItem - you'd just need to provide the appropriate SPI to determine what type is necessary and collect the necessary inputs.
A basic implementation would look something like:
public static class ItemFactory
{
public static Dictionary<string, Item> _Items = new Dictionary<string, Item>;
public static Item GetItem(string itemNumber)
{
if(!_Items.ContainsKey(itemNumber))
{
_Items[itemNumber] = new Item(itemNumber);
// Initialize item if necessary
}
return _Items[itemNumber];
}
}
The SerializedItem class is a representation of an actual item in that product line
Than an appropriate design is to make Item an ItemType and use composition instead of inheritance. So your second approach (with the change that SerializedItem does NOT inherit from Item) looks valid.
If Item is truly a non-instantiated base class then mark it as abstract and work through your concrete SerializedItem class ( and any other derived classes you may have ). If you only want a single Item in memory with a given item number then you might consider a Dictionary type collection indexed on the item number.
Related
I'm currently building a test application that manages parts for engineers, and I've hit a snag. I have a few different classes, including PartsModel and EngineerModel, and I want to update a list of parts that an engineer has, but I'm mindful of issues from either transposed parameters or from structuring the code in a way that unnecessarily couples to a particular class.
The two classes, with some relevant properties:
public class PartModel
{
public int PartId { get; private set; }
public string PartTitle { get; set; }
public string PartDescription { get; set; }
public int Quantity { get; set; }
public int MinimumStock { get; set; }
public void AddToStock (int quantityToAdd) {
Quantity += quantityToAdd;
}
public void RemoveFromStock (int quantityToRemove) {
Quantity -= quantityToRemove;
CheckMinimumStock();
}
}
public class EngineerModel
{
public int EngineerId { get; private set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<PartModel> PartsInStock { get; set; } = Factory.CreatePartsList();
}
As you can see, each engineer has a list of parts they have in stock via a List<PartModel>. I want to pass another list to this one so that I can update it respectively (incrementing or decrementing quantities, and then adding or removing parts to the list as necessary).
The first warning bell is that it takes two inputs of the same type, and is going to fill one from the other one (which isn't needed afterwards), so you're essentially modifying one input and destroying the other. To me, this presents a danger of the inputs getting transposed and the wrong list being either returned or updated (depending on whether it returns or just acts on the list). Because it removes items that have no quantity, it can't check the list length and just update the longer one, because there are possible cases where the engineer's list is shorter (maybe they're a new engineer, or maybe they just had a large shipment of parts sent when they were running low on stock). If it did just keep parts with quantity zero, then you're threatening scalability of both engineers and parts (not to mention any other objects that use the same operation).
So, put it as a method in the EngineerModel class and operate on PartsInStock, right? But what about when I want to use the same operation on other classes (e.g. if I have a list of parts associated to a work task)? Then I extract the method out to another class and... I'm passing the two lists as parameters in the method, so I'm back to where I was.
Am I being reasonable in not wanting to have two parameters of the same type, and how do I structure the code to deal with this, but without creating unnecessary coupling? If I'm not being reasonable, what am I overlooking?
Use an extension method
Thanks to #DavidBrowne-Microsoft for clarifying this. By defining an extension method for List<PartModel>, it only needs the one parameter - the list containing the updates (foreach below based on #Valentin's answer to this question).
public static class PartsHandler
{
public static List<PartModel> UpdateStockQuantitiesWith(this List<PartModel> stockToBeUpdated, List<PartModel> stockUpdates) {
foreach ( var part in stockUpdates )
{
var partToBeUpdated = stockToBeUpdated.FirstOrDefault(x => x.PartId == part.PartId);
if ( partToBeUpdated != null )
{ partToBeUpdated.Quantity += part.Quantity; }
else
{ stockToBeUpdated.Add(part); }
}
stockToBeUpdated.RemoveAll(x => x.Quantity <= 0);
return stockToBeUpdated;
}
}
Now any class that needs to implement this can simply call it in a method on the respective property. For example, in the EngineerModel class, it can operate on the PartsInStock property:
public void AddPartsToStock(List<PartModel> partsSent) {
PartsInStock.UpdateStockQuantitiesWith(partsSent);
}
This question already has answers here:
Generic list of generic objects
(3 answers)
Closed 4 years ago.
I have an object that I want to put into a List or Collection. Is there a way to do that without having the T specified?
I want to do something like this: List<CommonProperty<T>>
Here's the object for reference:
internal class CommonProperty<T>
{
public string Name { get; set; }
public PropType Type { get; set; }
public List<T> PossibleValues { get; set; }
private T _value;
public T Value
{
get { return _value; }
set
{
if (!_value.Equals(value))
{
_value = value;
}
}
}
}
No, you can't use open generic types like that.
You could have a List<CommonProperty<T>> within a context where T is already a type parameter:
public class Foo<T>
{
static void Bar()
{
// This is fine, but is not what you're looking for - it uses
// the type parameter T as the type argument
List<CommonProperty<T>> list = new List<CommonProperty<T>>();
}
}
Typically the solution here is to have a non-generic base class or interface which the generic class or interface derives from:
// Consider making it abstract
internal class CommonProperty
{
public string Name { get; set; }
public PropType Type { get; set; }
}
internal class CommonProperty<T> : CommonProperty
{
public List<T> PossibleValues { get; set; }
private T _value;
public T Value
{
get => _value;
set
{
// TODO: Is this really necessary?
if (!_value.Equals(value))
{
_value = value;
}
}
}
}
You can then create a List<CommonProperty>... although be aware that it's entirely possible that you could end up with an element which wasn't a CommonProperty<T> at all that way.
From the List<CommonProperty> you'd be able to retrieve the names and types of all the properties - but the values wouldn't be available without casting to the specific type. You could have an abstract object Value { get; set; } property in the base class, which was then overridden in the derived class, potentially - but it's not clear whether that's necessary or helpful in your use case.
I think the closest you can get is to define an interface to match an un-typed (specific) CommonProperty, using Object instead of the T. Then have your CommonProperty<T> implement that interface. Now you can use the interface with your list.
But this isn't great. You'll lose a lot of nice type checking, and have to do more casting. If this is the primary way you'll use these objects, there's not much point to having a generic class at all anymore.
It is not possible to put a mixture of generics instantiated with different type arguments into the same collection. Such collection would not be useful anyway, because the caller would be expected to supply T for each item at compile time.
Suppose you could do what you want, i.e.
// imagine that you could do this
List<CommonProperty<T>> mixedList = GetAllProperties();
You would be forced to supply T once you start using items from that list, i.e.
foreach (CommonProperty<T> prop in mixedList) {
... // ^^^
... // Here you would need to specify T, but you don't know which one
}
In other words, such list would be unusable.
A list of properties of a specific type, on the other hand, would be useful:
List<CommonProperty<string>> stringPropList = GetPropertiesOfType<string>();
foreach (CommonProperty<string> prop in stringPropList ) {
...
}
Therefore, one solution to your problem would be building GetPropertiesOfType<T> method returning List<CommonProperty<T>> bound to type T from the caller.
An alternative approach would be to make CommonProperty non-generic, and let the caller check the type at runtime:
internal class CommonProperty {
public string Name { get; set; }
public PropType Type { get; set; }
public List<T> GetPossibleValues<T> { get; set; }
private object _value;
public T GetValue<T>() {
return (T)_value;
}
public void SetValue<T>(T val) {
_value = val;
}
}
I'm trying to create a collection (list<> or IEnumerable<>) of a custom objet "InventorAttribue" that has 2 properties; Name and Value.
The "Value" property can be of various type so I thought of coding this object like this:
public class InventorAttribute<T> {
public InventorAttribute (string name, T value) {
Name = name;
Value = value;
}
public string Name { get; set; }
public T Value { get; set; }
}
Further I plan to use an "AttiributeSet" class to represent the final Autodesk Inventor AttributeSet to be stored in an Inventor's object. Here is the class and where my question stands, because of course, this code does not work as the type 'T' cannot be found (!)
public class AttributeSet
{
public AttributeSet(string category, string name {
Name = name;
Attributes = new List<InventorAttribute<T>>();
}
public string Category { get; set; }
public string Name { get; set; }
public List<InventorAttribute<T>> Attributes { get; set; }
public void AddAttribute(string name, T value){
Attributes.Add(new InventorAttribute<T>(name,value));
}
}
Question:
How can I manage to write this code, and being able to pass the "InventorAttribute.Value" type only at run time through the "AddAttribute" method.
Thanks in advance for greatly appreciated help.
Your AttributeSet class should be also parametrized:
public class AttributeSet<T>
NOTE: you cannot store InventorAttribute<T> parametrized with different T types in Attributes collection. Even if you could do that, how would you consume such collection? You will need to cast Value for each attribute to appropriate type. You will not have any benefits of having generic class here. So create non-generic InventorAttribute which will store values in property of object type.
You're probably imagining some form of inheritance. It doesn't exist here.
An InventorAttribute<string> is not a subclass of InventorAttribute<T>. Nor is it a subclass of InventorAttribute<object> (I mention this since it's usually people's next attempt to define the collection's item type). Each constructed generic type is effectively independent1.
If applicable, you may be able to introduce a new base class:
public abstract class InventorAttribute {
public string Name { get; set; }
public InventorAttribute (string name) {
Name = name;
}
}
public class InventorAttribute<T> : InventorAttribute {
public InventorAttribute (string name, T value) : base(name) {
Value = value;
}
public T Value { get; set; }
}
And you can now declare your collection to be of non-generic type InventorAttribute. But now you cannot access the Values until you cast to the more specific type.
1So far as the type system is concerned. As an implementation detail, the system is able to cleverly JIT only a single version of each method body that is applicable for all reference types. But that doesn't have any visible impact in the type system.
I have a large collection of automatically generated objects. Although they are all of different, non-related classes, all of the objects share some basic properties (name, id, etc.). I do not control the generation of these objects, so unfortunately I cannot take the ideal approach of implementing an interface. I would like to create a method in which I pass an arbitrary one of these objects and do something using these common properties.
The general idea would be something like:
someObj a = new someObj();
a.name = "sara";
diffObj b = new diffObj();
b.name = "joe";
string phrase = string.Format("I am with {0} and {1}",
getName(a), getName(b));
private string getName(object anyObjWithName)
{
return anyObjWithName.name;
}
though naturally this does not work.
I thought a generic method might hold the answer, but the only way I can see to call it with the current type is using genericMethod.Invoke , which still carries the same issue of not being able to resolve the properties of the passed object in the method. This is unlike Calling generic method with a type argument known only at execution time or How to call generic method with a given Type object? where only the type, or properties of the type, are used in the method, as opposed to properties of the object.
I am aware that this would be (very) prone to error, but I can guarantee that all objects encountered will have the common properties being manipulated.
I can guarantee that all objects encountered will have the common properties being manipulated
If that's the case, you can use dynamic:
private string getName(dynamic anyObjWithName)
{
return anyObjWithName.name;
}
Be aware that using any object that does not have a name property will not fail until run-time.
If you want to add a little bit of safety you can catch the RuntimeBinderException that gets thrown if the property does not exist:
private string getName(dynamic anyObjWithName)
{
try {
return anyObjWithName.name;
}
catch(RuntimeBinderException) {
return "{unknown}";
}
}
If you're unhappy with the performance using dynamic as mentioned by D Stanley, you could always try FastMember.
All you need to know to start using it is pretty much shown in the first 2 code examples.
You are creating a Rube Goldberg device there. You should just have all your data objects classes implement a single interface, then you can work on that. Much simpler and less error prone than fiddling with reflection.
The very fact that a lot of objects have common properties but don't share the same ancestry, on in the very least a common interface, shows that something is wrong with your design. Do rethink it.
Multiple ways to accomplish this, simplest probably is to create Interface and declare common methods there, have your object implement it, then change "getName" method take interface object
private string getName(IMyInterface anyObjWithName)
{
return anyObjWithName.name;
}
The correct way to do this is with an interface, if you own the types that you're working with
public interface IEntity
{
int ID { get; set; }
string Name { get; set; }
}
public class TypeOne : IEntity
{
public int ID { get; set; }
public string Name { get; set }
public string BespokePropertyOne { get; set;}
}
public class TypeTwo : IEntity
{
public int ID { get; set; }
public string Name { get; set; }
public float BespokePropertyTwo { get; set; }
}
static void Main(string[] args)
{
List<IEntity> entities = new List<IEntity>();
entities.Add(new TypeOne() { ID = 1, Name = "Bob", BespokePropertyOne = "blablabla" });
entities.Add(new TypeTwo() { ID = 2, Name = "Alice", BespokePropertyTwo = 5.4f });
foreach (IEntity entity in entities)
{
Console.WriteLine("ID: {0} Name: {1}", entity.ID, entity.Name);
}
}
This answer was written before the edit to the question stating that interfaces weren't possible in this case. Perhaps it can help someone else reading this question.
Interface:
interface Iname
{
string Name { get; set; }
}
Use interface:
class A : Iname
{
public string Name { get; set; }
}
class B : Iname
{
public string Name { get; set; }
}
The method:
string GetName(Iname o)
{
return o.Name;
}
Use:
A a = new A { Name = "First" };
B b = new B { Name = "Last" };
Text = GetName(a) + " " + GetName(b);
Is it possible to have a HasMany relationship of a basic type such as String, on an ActiveRecord class, without the need for creating another entity such as (TodoListItem) to hold the value.
[ActiveRecord]
public class TodoList
{
[PrimaryKey]
public int Id
{
get { return _id; }
set { _id = value; }
}
[HasMany(typeof(string)]
public IList<string> Items
{
get { return _items; }
set { _items= value; }
}
}
Can anyone help?
Yes, you can do this. You can map a one-to-many relation to a built-in or simple type (value type or string) rather than a persisted type.
You'll need to specify the ColumnKey, Table and Element params in the HasMany attribute declaration to get it to wire up properly. You have to have a surrogate key column so the AR can handle updates and cascades, and then Element tells AR which column in the table holds the simple value it will use to make the list.
[HasMany(typeof(string), Table="ToDoList_Items",
ColumnKey = "ListItemID", Element = "Item")]
public IList<string> Items { get; set; }
(or something similar - I haven't got a compiler handy on this box to check it; but per the API docs it ought to work.)
Speaking of which, if you haven't already had a look, http://api.castleproject.org is kinda indispensible for any work with the Castle stack.
In ActiveRecord, your types map to a record in a table (by default). It seems like you are confusing how this type should map to your table.
The MyClass type should have a definition something like this (excluding the PK settings):
[ActiveRecord(Table = "MyTable")]
public class MyClass : ActiveRecordBase<MyClass>
{
[Property]
public int Id { get; set; }
[Property]
public int MyClassId { get; set; }
[Property]
public string ListItem { get; set; }
}
Then, to load the list:
public void LoadMyClasses()
{
MyClass[] results = MyClass.FindAll();
}
I'd suggest you spend some time with the ActiveRecord documentation (or tutorial) as that should also help clear up any confusion.