Hi I have an abstract class Item. Classes like Food, Weapon, etc inherit by this class. All informations about this items are stored in the database, work of C# Code is match the exact class and match it by Enum which is also stored in the database column as integer. My problem is this stupid code wherever I have to use methods of Food, Weapon etc classes
if ((ItemType)userItem.ItemType == ItemType.Food)
{
Food food = new Food(userItem);
food.UseItem(sender);
}
else if ((ItemType)userItem.ItemType == ItemType.Weapon)
{
Weapon weapon = new Weapon(userItem);
weapon.UseItem(sender);
}
In the parameter of constructor of Food, Weapon etc. classes is the the object from database to let know object about its fields.
Is some kind of stuff that will help me to match this types without this code? It really annoys me when I'm looking at it.
You can use factory or creational method to create specific type of item:
public Item CreateItem(UserItem userItem)
{
var itemType = (ItemType)userItem.ItemType;
switch(itemType)
{
case ItemType.Food: return new Food(userItem);
case ItemType.Weapon: return new Weapon(userItem);
// etc
default:
throw new NotSupportedException($"Item type {itemType} is not supported");
}
}
Then use this method to create items and use them. E.g. your current code will look like:
var item = CreateItem(userItem);
item.UseItem(sender); // you don't care about specific type of item
Note: EF can use discriminator column to create entities of appropriate type automatically.
Just register building actions one time:
var builder = new ItemBuilder()
.RegisterBuilder(ItemType.Food, () => new Food())
.RegisterBuilder(ItemType.Weapon, () => new Weapon());
and use it later like this:
var item1 = builder.Build(ItemType.Food);
item1.UseItem(sender)
and here a builder code:
public class ItemBuilder
{
public ItemBase Build(ItemType itemType)
{
Func<ItemBase> buildAction;
if (itemBuilders.TryGetValue(itemType, out buildAction))
{
return buildAction();
}
return null;
}
public ItemBuilder RegisterBuilder(ItemType itemType, Func<ItemBase> buildAction)
{
itemBuilders.Add(itemType, buildAction);
return this;
}
private Dictionary<ItemType, Func<ItemBase>> itemBuilders = new Dictionary<ItemType, Func<ItemBase>> ();
}
Another option use a DI container like unity or somth:
UnityContainer.RegisterType<IItemBase, Food>("ItemType.Food");
UnityContainer.RegisterType<IItemBase, Weapon>("ItemType.Weapon");
and resolve
var item1 = UnityContainer.Resolve<IItemBase>(ItemType.Food.ToString());
Related
I don't know if this can be possible, but I will try to explain myself the best way that I can. Thank you for your help.
I'm trying to fit these entity objects in an integration with a third-party.
My problem is, I have these 3 entity type from the third-party, ObjectType1, ObjectType2, ObjectType3 object classes, all of these have the same base, so, my idea in order to no repeat the code several times was to create a fuction that accepts one generic list and inside of it, try to cast to the object type that I want and used to save the information in my principal entity.
I don't know if this can be possible or what would you recommend me to do, because what I'm trying to do is not to repeat the code several times.
Thank you again.
public static List<ItemsToSave> TestList<T>(List<T> listOfT)
{
var itemsToSave = new List<ItemsToSave>();
foreach (var item in listOfT)
{
object obj = null;
switch (listOfT)
{
case List<ObjectType1>:
obj = item as ObjectType1;
break;
case List<ObjectType2>:
obj = item as ObjectType2;
break;
case List<ObjectType3>:
obj = item as ObjectType2;
break;
}
var itemtosaveEntity = new ItemsToSave()
{
FirstName = obj.FirstName,
MiddleName = obj.MiddleName,
LastName = obj.LastName,
};
itemsToSave.Add(itemtosaveEntity);
}
return itemsToSave;
}
Try this
var listOfT = new List<BaseType>();
List<ItemsToSave> itemsToSave = ConvertList(listOfT);
public static List<ItemsToSave> ConvertList<T>(List<T> listOfT) where T : BaseType
{
return
listOfT
.Select(i => new ItemsToSave()
{
FirstName = i.FirstName,
MiddleName = i.MiddleName,
LastName = i.LastName,
})
.ToList();
}
You can define your generic function like this:
public static List<ItemsToSave> TestList<T>(List<T> listOfT) where t : BaseClass
This is is called a 'Where generic constraint', read more Here
That way the type you have in your function will be BaseClass and you can access all properties defined in that class.
I have the following code where i am adding some class objects in an Array.
Object[] ArrayOfObjects = new Object[] {typeof(Person), typeof(Company)};
Now if i want to iterate through my class items, how can i convert each item back to its original Type (such as Person and Company)? This might be possible using Reflection but i want to find out if C# has some built in functionality to achieve this.
foreach (var item in ArrayOfObjects)
{
// TODO Convert item back to Original Type (Person or Company)
// I am doing something like this but not working
var person = Convert.ChangeType(item, typeof(Person));
//I can not do this too as hardcoding the type inside the loop makes no sense
var person = item as Person; //I need to convert item as Person or Company so that i can automate the tasks here.
}
Much Thanks in advance.
Making some assumptions about your use case, you might benefit from using an interface to eliminate the need to convert the objects at all
Say you need to do the shared method Foo which belongs to both Company and Person
public interface ObjectWithFoo{
void Foo();
}
public class Person : ObjectWithFoo{
...
}
public class Company: ObjectWithFoo{
...
}
Then in your main code you create a list of ObjectWithFoo
ObjectWithFoo[] myArray = new ObjectWithFoo[]{new Person(), new Company()}
And then in your loop
foreach(var objectWithFoo in myArray)
objectWithFoo.Foo();
This way you don't need to cast at all, you can just use use the interface for everything. The added benefit is that it becomes more clear what your array is meant to be used for to yourself and others - it is used for only methods/attributes belonging to your interface. If you use an array of objects people can easily add an unsupported type or start using the list elsewhere and make your code a bit chaotic.
using System;
namespace PatternMatching
{
class Person
{
public void PersonMethod() => throw new Exception();
}
class Company
{
public void CompanyMethod() => throw new Exception();
}
class Program
{
static void Main(string[] args)
{
Object[] ArrayOfObjects = { new Person(), new Company() };
foreach (var item in ArrayOfObjects)
{
if (item is Person person)
{
person.PersonMethod();
}
if (item is Company company)
{
company.CompanyMethod();
}
}
}
}
}
Use pattern matching (C# 8.0+)
https://learn.microsoft.com/en-us/archive/msdn-magazine/2019/may/csharp-8-0-pattern-matching-in-csharp-8-0
You can use even switch pattern for that.
I have an ASP.NET MVC (Not Core) project where I have run into some problems, and I think finally getting around to learning how to properly use generics could be a solution to my problems.
My case is that I have a SQL connection, that returns data and depending on the result of one field in the SQL, I want to use two different models. The models have a lot of properties in common so I thought the best practice would be to create a method that selects which of the models to create, fill in the differences, return the model and then continue to fill in the "common" properties.
I have tried to read a little on Generics but I am quite new to this so I haven't made any big strides.
My code example looks like this:
public ResultVM MainClass()
{
var resultData = new ResultVM();
// ... SQL returns data
while (reader.Read())
{
resultData.Add(reader);
}
return resultData;
}
public object CreateObject(SqlDataReader indata)
{
if((indata["Associate"].ToString()) == "0")
{
var individual = new Individual();
}
else
{
var group = new Group();
}
return object;
}
How can I dynamically (depending on the value of Associate field) create an individual or a group?
I suggest working directly with System.Type in your case. Here can be multiple more elegant solutions of your problem, depending of what you actually need:
indata.GetFieldType(int ordinal) will return the .NET type of your field
Serialize data with type handling, then you can simply get type after non generic deserialization. For example:
var result = JsonConvert.DeserializeObject(dataJson);
result will have Type of your actual object type. You can check it writing result.GetType() and create an object of this type. For more advanced use see Activator.CreateInstance(...)
For the most cases using interface is the best way:
interface ISomething
{
// ...
}
class Individual : ISomething
{
// ...
}
class Group : ISomething
{
// ...
}
Then you cat build your non generic method this way:
public ISomething CreateObject(SqlDataReader indata)
{
if((indata["Associate"].ToString()) == "0")
return new Individual();
else
return new Group();
}
Your generic object creation may look something like this:
public T CreateObject<T>(SqlDataReader indata)
{
var o = new T();
return o;
}
Where T is type, that you pass outside this method: CreateObject<YourType>(indata)
T can be any Type you want including Interfaces. Also working with generics you may want check types by using is keyword, but I recommend using interfaces and avoid is checks at all.
Example:
if(yourObject is YourType)
{
(yourObject as YourType).SomeSpecificToYourTypeMethod();
// ...
}
What about implementing an interface with all the common properties?
Something like
interface IInterface
{
string CommonProperty1 { get; set; }
string CommonProperty2 { get; set; }
}
class Individual : IInterface
{
// ...
}
class Group : IInterface
{
// ...
}
public IInterface CreateObject(SqlDataReader indata)
{
if((indata["Associate"].ToString()) == "0")
{
var individual = new Individual();
// ...
return individual;
}
else
{
var group = new Group();
// ...
return group;
}
}
My CTO (Chief Technical Officer) asked me to come up with a way where he could write one single function in the base class, and have access to the all the properties of the child class. Here is what I came up with -
Base Class
class Assets
{
public Assets getPropertyVal(Assets asObj)
{
PropertyInfo[] propInfos = asObj.GetType().GetProperties();
string strAttributeValue = "10";
foreach (PropertyInfo propInfo in propInfos)
{
// Getting the value
var propValue = propInfo.GetValue(asObj, null);
// Setting the value
propInfo.SetValue(asObj, Convert.ChangeType(strAttributeValue, propInfo.PropertyType), null);
}
return asObj;
}
}
Child Class
class House : Assets
{
public int rooms{get; set;}
}
Program.cs file
class Program
{
static void Main(string[] args)
{
House hsObj = new House();
hsObj.rooms = 5;
Assets asObj = hsObj.getPropertyVal(hsObj);
// Returns asObj as JSON
}
}
Now this works fine, but I was just wondering if there was a better way to do this in C#.
Note that we do not know what properties will be present in the child class, so this will have to be determined at run-time.
UPDATE : Making it clear, I was just wondering if there is a better way to access the child class properties, one without using reflection. The important point to note is that we have no idea what properties a child class may have.
UPDATE #2 : I am working with a product that has many entities. These entities have different properties. I want to be able to access and work with all these properties in one single place. This function is exactly that. It's that one single place from where I can access all the data.
First, your Program.cs doesn't actually "do" what you say you want. It sounds like you want a program so that you can do this:
Asset myAsset = new House();
myAsset.Rooms = 5;
But, why would you even want to do that anyway? If your asset isn't a House, it will throw an exception, so you will need to check that first:
if (myAsset is House)
myAsset.Rooms = 5;
At that point, you might as well just cast it to a House though. It sounds like you may want to use a PropertyBag or Dictionary instead of inheritance.
I think what you are describing is this. Note that option 1 doesn't really restrict which properties can be used on which classes, so I'm guessing this won't really work for your specific case.
// Option 1, a Property Bag (Note: this replaces the properties on the classes)
class Asset
{
Dictionary<string, object> myPropertyBag = new Dictionary<string, object>();
public T GetProperty<T>(string property)
{
// This throws if the property doesn't exist
return (T)myPropertyBag[property];
}
public void SetProperty<T>(string property, T value)
{
// This adds the property if it doesn't exist
myPropertyBag[property] = (object)value;
}
}
// Option 2, use a switch and override this function in derived classes
class Asset
{
public int SomePropertyOnAsset { get; set; }
public virtual T GetProperty<T>(string property)
{
switch (property)
{
case "SomePropertyOnAsset": return this.SomePropertyOnAsset;
default: throw new ArgumentException("property");
}
}
public virtual void SetProperty<T>(string property, T value)
{
switch (property)
{
case "SomePropertyOnAsset": this.SomePropertyOnAsset = (int)value;
default: throw new ArgumentException("property");
}
}
}
class House : Asset
{
public int Rooms { get; set; }
public virtual T GetProperty<T>(string property)
{
switch (property)
{
case "Rooms": return this.Rooms;
default: return base.GetProperty<T>(property);
}
}
public virtual void SetProperty<T>(string property, T value)
{
switch (property)
{
case "Rooms": this.Rooms = (int)value;
break;
default: base.SetProperty<T>(property, value);
break;
}
}
}
Then, this is how you use them:
// Option 1
Asset asset = new House();
asset.SetProperty("Rooms", 5);
var rooms = asset.GetProperty<int>("Rooms");
// Option 2
Asset asset = new House();
asset.SetProperty("Rooms", 5);
asset.SetProperty("SomePropertyOnAsset", 10);
asset.SetProperty("SomethingElse", 15); // Throws ArgumentException
A 3rd option is to make Asset a DynamicObject.
http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject.aspx
If you can't or don't want to make a major change to your Asset base class or touch every entity, you will probably need to use reflection.
Luke Gravitt is probably right. You might just want to cast it to a house.
House myHouse = asObj as House;
if ( myHouse != null )
{
// do some fun house stuff
}
Yacht myYacht = asObj as Yacht;
if ( myYacht != null )
{
// put on monocle
}
I have a csv file where each row is a different type of record. I am parsing the csv and want to store the rows (varied types of records) in various types of custom classes.
At each row i need to instantiate a different class based on the record type.
So taken from other reflection examples, I have the code below;
Type type = Type.GetType("myNamespace." + className);
object recordclass = Activator.CreateInstance(type);
so i have an object named recordclass of the correct type, but how do I use it?
all I really want to do is access the properties of the class and populate the row data, and then later add to a container class.
I guess im missing something about the runtime nature of reflection. Please help me connect the dots!
Hope that all makes sense!
TIA,
Gary
With the example you give you could cast your object to the actual type you need:
Type type = Type.GetType("myNamespace." + className);
object recordclass = Activator.CreateInstance(type);
var record = recordClass as ConcreteRecordType;
if(record != null)
record.Name = csv["Name"];
Alternatively look into using a Factory to return populated record objects:
public class RecordFactory
{
RecordBase ParseCsvRow(string[] columns)
{
const int typeDescriminatorColumn = 0;
switch (columns[typeDescriminatorColumn])
{
case "RecordTypeA":
return new RecordTypeA(columns[1], columns[2], ...);
case "RecordTypeB":
return new RecordTypeB(columns[1], columns[2], ...);
default:
throw new InvalidOperationException("Unexpected descriminator: " + columns[typeDescriminatorColumn]);
}
}
}
If you want to store values into your recordclass's property via reflection use this
var property = type.GetProperty(propertyName);
property.SetValue(recordclass,value,null);
If you read the docs, you will see, Type.GetType requires a full qualified type name.
If I understand the problem correctly you have a file which contains text values for records. Each record is stored in a single line, and the start of each line is an identifier to say which kind of record is to be built.
It is possible to use reflection for this but not really neccessary. The problem with using reflection is that you need to know all the properties for the different record types in order to access them by name. At this point, you may as well be working with typed objects but if you are using the a single routine to create all the records (using CreateInstance()) all you have is an untyped object.
Another solution is to have a set of routines each of which take in, say an IEnumerable (the input line split by the comma, excluding the record id) and return an object (or a record, if you have a base record class) and use a factory to select which routine to use for each row.
You register the routines with the factory by some ID (the first field in the record as you are doing is good, it can be the record class name but doesn't have to be) and the iterate through the CSV lines, using the first piece to select the method from the factory and building the record.
Hopefully the example will explain a bit better :? Sorry about the volume of code
The builders in the example just return empty records but populating them from the row pieces should be easy. Another version is to just pass in the row, or a set of rows if a record can cover a number of rows (a but more complicated if the records take in different numbers of rows)
hth,
Alan.
public string[] Input = new[]{
"R1, F1, F2, F3",
"R2, F2, F4",
"R3, F2",
"R3, F2",
"R4, F1, F2, F3, F4"
};
public class RecordOne {
}
public class RecordTwo {
}
public class RecordThree {
}
public class RecordFour {
}
public class BuilderFactory {
public BuilderFactory() {
Builders = new Dictionary<string, Func<IEnumerable<string>, object>>();
}
private Dictionary<string, Func<IEnumerable<string>, object>> Builders { get; set; }
public void RegisterBuilder(string name, Func<IEnumerable<string>, object> builder) {
Builders.Add(name, builder);
}
public Func<IEnumerable<string>, object> GetBuilder(string name) {
return Builders[name];
}
}
[TestMethod]
public void LoadRecords() {
var factory = new BuilderFactory();
factory.RegisterBuilder("R1", BuildRecordOne);
factory.RegisterBuilder("R2", BuildRecordTwo);
factory.RegisterBuilder("R3", BuildRecordThree);
factory.RegisterBuilder("R4", BuildRecordFour);
var output = Input.Select(line => {
var pieces = line.Split(',').Select(val => val.Trim());
var builder = factory.GetBuilder(pieces.First());
return builder(pieces.Skip(1));
});
Assert.IsTrue(new[] {typeof(RecordOne),
typeof(RecordTwo),
typeof(RecordThree),
typeof(RecordThree),
typeof(RecordFour)}.SequenceEqual(output.Select(rec => rec.GetType())));
}
private static RecordOne BuildRecordOne(IEnumerable<string> pieces) {
return new RecordOne();
}
private static RecordTwo BuildRecordTwo(IEnumerable<string> pieces) {
return new RecordTwo();
}
private static RecordThree BuildRecordThree(IEnumerable<string> pieces) {
return new RecordThree();
}
private static RecordFour BuildRecordFour(IEnumerable<string> pieces) {
return new RecordFour();
}