Storing functions in C# class properties? - c#

I need to create a list containing Device objects. These objects have properties that describe their Name, Unit and Conversion. Conversion is a bit special as this is supposed to be a function. For example: If I had a temperature sensor measuing in farenheit, its conversion method should calculate and return the value in celsius. And if I have a moisture sensor, its conversion will be different etc. Below is an example of how I have tried doing this but it doesn't work. I get errors saying that only assignment is allowed on Conversion.
private class Device
{
public string Name { get; set; }
public Action Conversion { get; set; }
public string Unit { get; set; }
}
public static object ParseDecentLab(byte[] bytes)
{
List<Device> deviceList = new List<Device>()
{
new Device()
{
Name = "battery-voltage",
Conversion = (x) => x / 1000,
Unit = "V"
},
new Device()
{
Name = "air-temperature",
Conversion = (x) => (175 * x / 65535) - 45,
Unit = "°C"
}
};

Try this code:
use Func instead of Action.
Func can return value where as Action Can't.
private class Device
{
public string Name { get; set; }
public Func<double,double> Conversion { get; set; }
public string Unit { get; set; }
}
public static object ParseDecentLab(byte[] bytes)
{
List<Device> deviceList = new List<Device>()
{
new Device()
{
Name = "battery-voltage",
Conversion = (x) => x / 1000,
Unit = "V"
},
new Device()
{
Name = "air-temperature",
Conversion = (x) => (175 * x / 65535) - 45,
Unit = "°C"
}
};
}

You want Func<double, double> instead of Action; given double (e.g. 4.5 Volt) return double.
x => x / 1000
On the contrary, Action takes no arguments and returns nothing: () => {...}
Code:
// Let's implement immutable class (assigned once, never change)
// in order to prevent occasional errors like device.Name = ...
private class Device {
public string Name { get; }
public Func<double, double> Conversion { get; }
public string Unit { get; }
// Let's validate the input (at least, for null)
public Device(string name, Func<double, double> conversion, string unit) {
if (null == name)
throw new ArgumentNullException(nameof(name));
else if (null == conversion)
throw new ArgumentNullException(nameof(conversion));
else if (null == unit)
throw new ArgumentNullException(nameof(unit));
Name = name;
Conversion = conversion;
Unit = unit;
}
}
...
List<Device> deviceList = new List<Device>() {
new Device("battery-voltage", x => x / 1000, "V"),
new Device("air-temperature", x => (175 * x / 65535) - 45, "°C"),
};
Possible usage:
// What device should we use if we want °C unit?
Device temperature = deviceList
.FirstOrDefault(item => item.Unit == "°C");
byte[] dataToConvert = new byte[] {123, 45, 79};
// Device found
if (temperature != null) {
// Converting: for each value in dataToConvert we obtain corresponding t value
foreach (var value in dataToConvert) {
double t = temperature.Conversion(value);
...
}
}
Or you can even have an array of converted values (double[]) with a help of Linq:
byte[] dataToConvert = new byte[] {123, 45, 79};
// Let's throw exception if device has not been found
Device temperature = deviceList
.First(item => item.Unit == "°C");
double[] results = dataToConvert
.Select(v => temperature.Convert(v))
.ToArray();

Related

How can i create an object from this single string i've split?

im trying to make a report about supplies used by connecting to the technical service database and filtering supply audits, inside audits what i care about is ActionNotes which is a single long string formatted like this:
New Supplie Mobile added: /n Id: 1/n Name: Bateria /n Stock. 0/n Minimum Stock: 10/n IdSquad: 1/n IdSupplie: 1/n
I've managed to write this code which creates an array of strings after splitting and filtering the values that i don't need and comes out something like this:
private void ImportarServicioTecnico()
{
var auditList = db3.Audit.ToList();
var suppliesList = (from t in auditList where t.ActionNotes.ToLower().Contains("new supplie mobile added") select t).ToList();
foreach (var i in suppliesList)
{
InsumosST o = new InsumosST();
var note = i.ActionNotes;
Debug.WriteLine("Audit ID: " + i.Id.ToString() + " Date: " + i.AuditDate);
string[] lines = Regex.Split(note, "/n");
foreach (var l in lines)
{
var checkstring = l.ToLower();
string actual = l;
if (checkstring.Contains("new supplie mobile added") || checkstring.Contains("description:")) {continue;}
if (checkstring.Contains("stock."))
{
int pos2 = actual.IndexOf(".");
Debug.WriteLine(actual.Substring(pos2 + 1));
continue;
}
int pos = actual.IndexOf(":");
Debug.WriteLine(actual.Substring(pos + 1));
}
}
}
Audit ID: 21 Date: 15-11-2021 10:43:59
1 Bateria 0 1 0 1 1
The question being is: is it possible to create an object from my db model with this code?
This is my model:
public partial class InsumosST
{
public int id { get; set; }
public string supply { get; set; }
public Nullable<System.DateTime> entrydate { get; set; }
public string squad { get; set; }
}
enter code here
Sure.. Let's have some dictionary of delegates that assign values to the properties of InsumosST, split the line on /n then again on : to get an id and value, look for the id in the dictionary and if it;s there, call the delegate passing the value
private void ImportarServicioTecnico()
{
var auditList = db3.Audit.ToList();
var suppliesList = (from t in auditList where t.ActionNotes.ToLower().Contains("new supplie mobile added") select t).ToList();
//make a dictionary of the string we look for and the "method" to execute if we find that string
//the methods are one-liners that set the appropriate value on the object
var d = new Dictionary<string, Action<string, InsumosST>();
d["id"] = (v, i) => i.id = int.TryParse(v, out int z) ? z : SOME_DEFAULT_ID_HERE;
d["idsupplie"] = (v, i) => i.supply = v;
d["idsquad"] = (v, i) => i.squad = v;
foreach (var i in suppliesList)
{
var o = new InsumosST();
var bits = i.ActionNotes.Split("/n");
foreach (var line in lines)
{
var bits = line.Split(new[]{':','.'}, 2); //line is e.g. "idsupplie: 1"
if(bits.Length < 2) continue; //if we don't have an id and value
var id = bits[0].ToLower(); //e.g. "idsupplie"
var val = bits[1].Trim(); //e.g. "1"
if(d.TryGetValue(id, out var m)) //if we know this id, e.g. "id", "idsupplie", "idsquad"
m(val, o); //call the delegate; it will assign val to the right property of o
}
context.InsumosSts.Add(o);
}
context.SaveChanges();
}
I suppose the hardest thing to "get" about this is if you're not really used to delegates; you've used them (conceptually where t.ActionNotes.ToLower().Contains("new supplie mobile added") is such a thing => it's a method that produces a bool from some input value determined by from) but it doesn't mean you'd be familiar with them.
Simply put, delegates are a way of storing method code in a variable just like we store data. You can assign a method to a variable, and then execute the variable to run the code:
var someMethod = () => Debug.WriteLine("hello");
someMethod(); //prints hello
You can make them take parameters:
var someMethod = (string s) => Debug.WriteLine(s);
someMethod("hello"); //prints hello
So all we did here was make up a bunch of methods that assign props to a passed-in InsumosSt
var x = (InsumosST i, string v) => i.idsupplie = v;
x(someInsumosObject, "hello"); //assigns "hello" to someInsumosObject.idsupplie property
It means to support more properties, you just add them to the InsumosST and
write a dictionary entry that assigns them:
public partial class InsumosST
{
public int id { get; set; }
public string supply { get; set; }
public Nullable<System.DateTime> entrydate { get; set; }
public string squad { get; set; }
//new!
public string Name {get;set;}
}
d["name"] = (v, i) => i.Name = v;

How can i convert string to double ? I'm getting exception

for(int i = 0; i < Rect.rectangles.Count; i++)
{
if (Rect.rectangles[i].transform != null && Rect.rectangles[i].transform != "")
{
string firstNumber = Rect.rectangles[i].transform.Substring(18, 10);
string secondNumber = Rect.rectangles[i].transform.Substring(7, 10);
double a = Convert.ToDouble(firstNumber);
double b = Convert.ToDouble(secondNumber);
}
}
The first number is 0.49052715 and the Convert.ToDouble of a is fine.
But when it's getting to the line:
double b = Convert.ToDouble(secondNumber);
I'm getting exception:
FormatException: Unknown char: ,
But there is no any char/s at all the string in secondNumber is: 0.87142591
This is the whole string in transform: matrix(0.87142591,0.49052715,-0.49052715,0.87142591,0,0)
And i'm extracting the first two numbers: 0.49052715 and 0.87142591 and then trying to convert them to double. But getting the exception.
The code including the Rect.rectangles definition:
private void ParseSvgMap()
{
XDocument document = XDocument.Load(#"C:\Users\mysvg\Documents\my.svg");
Rect.rectangles = document.Descendants().Where(x => x.Name.LocalName == "rect").Select(x => new Rect()
{
style = Rect.GetStyle((string)x.Attribute("style")),
id = (string)x.Attribute("id"),
width = (double)x.Attribute("width"),
height = (double)x.Attribute("width"),
x = (double?)x.Attribute("width"),
y = (double?)x.Attribute("width"),
transform = x.Attribute("transform") == null ? "" : (string)x.Attribute("transform")
}).ToList();
for(int i = 0; i < Rect.rectangles.Count; i++)
{
if (Rect.rectangles[i].transform != null && Rect.rectangles[i].transform != "")
{
string firstNumber = Rect.rectangles[i].transform.Substring(18, 10);
string secondNumber = Rect.rectangles[i].transform.Substring(7, 10);
double a = Convert.ToDouble(firstNumber);
double b = Convert.ToDouble(secondNumber);
float degree = FindDegree(a, b);
}
}
}
public static float FindDegree(double a, double b)
{
float value = (float)((System.Math.Atan2(a, b) / System.Math.PI) * 180f);
if (value < 0) value += 360f;
return value;
}
public class Rect
{
public static List<Rect> rectangles { get; set; }
public Dictionary<string, string> style { get; set; }
public string id { get; set; }
public double width { get; set; }
public double height { get; set; }
public double? x { get; set; }
public double? y { get; set; }
public string transform { get; set; }
public double degree { get; set; }
public static Dictionary<string, string> GetStyle(string styles)
{
string pattern = #"(?'name'[^:]+):(?'value'.*)";
string[] splitArray = styles.Split(new char[] { ';' });
Dictionary<string, string> style = splitArray.Select(x => Regex.Match(x, pattern))
.GroupBy(x => x.Groups["name"].Value, y => y.Groups["value"].Value)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
return style;
}
}
Code like .Substring(18, 10) is hopelessly brittle and (as you have discovered) is an accident waiting to happen. Numbers aren't guaranteed to be the same length. Just look at the sample data you provided.
You should use something like a regex to extract the numbers from the strings before attempting to parse them.
var regex = new Regex(#"matrix\((?<num>-?(?:\d|\.)+)(?:,(?<num>-?(?:\d|\.)+))*\)");
var data = "matrix(0.87142591,0.49052715,-0.49052715,0.87142591,0,0)";
var values = regex
.Matches(data)
.Cast<Match>()
.SelectMany(m => m.Groups["num"]
.Captures
.Cast<Capture>()
.Select(c=>c.Value))
.Select(double.Parse)
.ToList();
Then pick out the numbers you want with values[0] and values[1].
Its doomed to fail.
I would use following regex:
string test = "matrix(0.1234,0.23233433,0.34,-3343,-3.33)";
Regex reg = new Regex("[-]?[0-9]+(\\.[0-9]+)?");
MatchCollection coll = reg.Matches(test);
You can write a method along the lines of :
public double fixString(string incoming)
{
Double a;
if(incoming.Contains(','))
{
string fixedNum = incoming.Remove(incoming.IndexOf(','));
a = Convert.ToDouble(fixedNum);
return a;
}
else
{
a = Convert.ToDouble(incoming);
return a;
}
}

List<object> select use multiple values

I want to find a specific value from a List with the method select.
My code :
public class Calc
{
public int IdCalc { get; set; }
public double Result { get; set; }
public int Number { get; set; }
}
public class Program
{
static void Main()
{
Calc myC1 = new Calc();
List<Calc> liCalc = new List<Calc>();
myC1.IdCalc = -1;
myC1.Result = 20.2;
myC1.Number = 1;
Calc myC2 = new Calc();
myC2.IdCalc = 22;
myC2.Result = 20.2;
myC2.Number = 2;
liCalc.Add(myC1);
liCalc.Add(myC2);
double getResult = ((Calc)(liCalc.Select(Calc => Calc.IdCalc = 22 && Calc.Number = 2))).Result;
Console.ReadKey();
}
}
As you can see my List contains two objects: myC1 and myC2.
I just want to find the value of Result when IdCalc = 22 and Number = 2 thats why I tried to use Select but it's not working with two parameters.
You could use Where, which lets you filter results based on some criteria, however that will return an IEnumerable<Calc>. Since you are only looking for a single result, you should use First which also takes a predicate and only returns the first Calc:
Calc myCalc = liCalc.First(c => c.IdCalc == 22 && c.Number == 2);
double result = myCalc.Result;
This will throw an exception if there is nothing that matches the filter, though. If you're worried about that, use FirstOrDefault which will return null if there is no match.
public class Calc
{
public int IdCalc { get; set; }
public double Result { get; set; }
public int Number { get; set; }
}
public class Program
{
static void Main()
{
Calc myC1 = new Calc();
List<Calc> liCalc = new List<Calc>();
myC1.IdCalc = -1;
myC1.Result = 20.2;
myC1.Number = 1;
Calc myC2 = new Calc();
myC2.IdCalc = 22;
myC2.Result = 20.2;
myC2.Number = 2;
liCalc.Add(myC1);
liCalc.Add(myC2);
double getResult = liCalc.First(item => item.IdCalc == 22 && item.Number == 2).Result; //Note that this will throw an exception if no item in the list satisfies the condition.
Console.ReadKey();
}
You could use the following statement
double getResult = liCalc.Where(Calc => Calc.IdCalc = 22 && Calc.Number = 2))).Select(y=>y.Result).FirstOrDefault();
Essentially using Where() followed by Select().

Passing property as parameter

I am creating a merit function calculator, which for the uninitiated takes a selection of properties, and calculates a value based on how close those properties are to some idealized values (the merit function). This then enables the user to find an item that most closely matches their requirements.
This is the code I'd like to use:
public class MeritFunctionLine
{
public Func<CalculationOutput, double> property { get; set; }
public double value { get; set; }
public ComparisonTypes ComparisonType { get; set; }
}
public class MeritFunction
{
public List<MeritFunctionLine> Lines { get; set; }
public double Calculate(CalculationOutput values)
{
double m = 0;
foreach (var item in Lines)
{
m += Math.Abs(values.property - item.value);
}
return m;
}
}
public class CalculationOutput
{
public double property1 { get; set; }
public double property2 { get; set; }
public double property3 { get; set; }
public double property4 { get; set; }
}
Obviously this doesn't compile as values doesn't contain a member called property, but here is an explanation of what I want to do:
Create a new MeritFunction
Add an arbitrary number of MeritFunctionLines to MeritFunction.Lines
The MeritFunctionLine.property should specify what property of CalculationOutput should be compared in MeritFunction.Calculate
i.e.
MeritFunction mf = new MeritFunction();
mf.Lines.Add(new MeritFunctionLine() { property = x => x.Property1, value = 90, comparisonType = ComparisonTypes.GreaterThan });
mf.Lines.Add(new MeritFunctionLine() { property = x => x.Property3, value = 50, comparisonType = ComparisonTypes.Equals });
CalculationOutput c1 = new CalculationOutput() { property1 = 1, property2 = 20, property3 = 150, property4 = 500 };
CalculationOutput c2 = new CalculationOutput() { property1 = 15, property2 = 32, property3 = 15, property4 = 45 };
double value1 = mf.Calculate(c1);
double value2 = mf.Calculate(c2);
I am not asking how to pass a property as a parameter into a function, which is prohibited by C#.
You almost have the right solution already - the only missing piece is how you use the MeritFunctionLine.property property to get the desired value from the CalculationOutput.
In your foreach loop, simply replace the calculation line with
m += Math.Abs(item.property(values) - item.value);
Edit:
Adding Genericity
To address Obsidian Phoenix's comment, you can use this with different classes by making both MeritFunction and MeritFunctionLine generic, so:
public class MeritFunctionLine<TCalcOutput>
{
public Func<TCalcOutput, double> property { get; set; }
public double value { get; set; }
public ComparisonTypes ComparisonType { get; set; }
}
public class MeritFunction<TCalcOutput>
{
public List<MeritFunctionLine<TCalcOutput>> Lines { get; set; }
public double Calculate(TCalcOutput values)
{
double m = 0;
foreach (var item in Lines)
{
m += Math.Abs(item.property(values) - item.value);
}
return m;
}
}
The rewritten usage example would be
MeritFunction<CalculationOutput> mf = new MeritFunction<CalculationOutput>();
mf.Lines.Add(new MeritFunctionLine<CalculationOutput>() { property = x => x.Property1, value = 90, comparisonType = ComparisonTypes.GreaterThan });
mf.Lines.Add(new MeritFunctionLine<CalculationOutput>() { property = x => x.Property3, value = 50, comparisonType = ComparisonTypes.Equals });
CalculationOutput c1 = new CalculationOutput() { property1 = 1, property2 = 20, property3 = 150, property4 = 500 };
CalculationOutput c2 = new CalculationOutput() { property1 = 15, property2 = 32, property3 = 15, property4 = 45 };
double value1 = mf.Calculate(c1);
double value2 = mf.Calculate(c2);
Some extra convenience
If you have many MeritFunctionLines to add, the syntax above can be a bit tedious. So as a bonus, let's change MeritFunction so that it can be initialized with the list initialization syntax. To do that, we need to make it IEnumerable and give it an Add function:
public class MeritFunction<TCalcOutput> : IEnumerable<MeritFunctionLine<TCalcOutput>>
{
public List<MeritFunctionLine<TCalcOutput>> Lines { get; set; }
public MeritFunction()
{
Lines = new List<MeritFunctionLine<TCalcOutput>>();
}
public void Add(Func<TCalcOutput, double> property, ComparisonTypes ComparisonType, double value)
{
Lines.Add(new MeritFunctionLine<CalculationOutput>
{
property = property,
value = value,
comparisonType = ComparisonType
});
}
public double Calculate(TCalcOutput values)
{
double m = 0;
foreach (var item in Lines)
{
m += Math.Abs(item.property(values) - item.value);
}
return m;
}
public IEnumerator<MeritFunctionLine<TCalcOutput>> GetEnumerator()
{
return List.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
Note that the Add method receives the parameters in a different order - you'll understand why when you look at the usage. Quite a bit of extra code, but now creating our MeritFunction is a bit nicer:
MeritFunction<CalculationOutput> mf = new MeritFunction<CalculationOutput>
{
{ x => x.Property1, ComparisonTypes.GreaterThan, 90 },
{ x => x.Property3, ComparisonTypes.Equals, 50 }
};
Note, all code untested. Use at own risk :)
It is possible, but it's not exactly pretty. You can make use of Expression<Func<double>> to pass in the property, then use reflection to pull the value back out.
NB: I have not coded this to accomodate for error scenarios, you may want to add additional checks.
class Program
{
static void Main(string[] args)
{
MeritFunction<CalculationOutput> mf = new MeritFunction<CalculationOutput>();
//Create an instance of the object for reference.
var obj = new CalculationOutput();
//Use Lambda to set the Property Expression on the Line, pointing at the Property we are interested in.
mf.Lines.Add(new MeritFunctionLine() { PropertyExpression = () => obj.property1, value = 90, ComparisonType = ComparisonTypes.GreaterThan });
mf.Lines.Add(new MeritFunctionLine() { PropertyExpression = () => obj.property3, value = 50, ComparisonType = ComparisonTypes.Equals });
CalculationOutput c1 = new CalculationOutput() { property1 = 1, property2 = 20, property3 = 150, property4 = 500 };
CalculationOutput c2 = new CalculationOutput() { property1 = 15, property2 = 32, property3 = 15, property4 = 45 };
double value1 = mf.Calculate(c1);
double value2 = mf.Calculate(c2);
Console.WriteLine(value1);
Console.WriteLine(value2);
}
}
public class MeritFunctionLine
{
//Capture an expression representing the property we want.
public Expression<Func<double>> PropertyExpression { get; set; }
public double value { get; set; }
public ComparisonTypes ComparisonType { get; set; }
}
public class MeritFunction<T>
{
public List<MeritFunctionLine> Lines { get; set; }
public MeritFunction()
{
Lines = new List<MeritFunctionLine>();
}
public double Calculate(T values)
{
double m = 0;
foreach (var item in Lines)
{
//Get the Value before calculating.
double value = ExtractPropertyValue(item, values);
m += Math.Abs(value - item.value);
}
return m;
}
/// <summary>
/// Take the Provided Expression representing the property, and use it to extract the property value from the object we're interested in.
/// </summary>
private double ExtractPropertyValue(MeritFunctionLine line, T values)
{
var expression = line.PropertyExpression.Body as MemberExpression;
var prop = expression.Member as PropertyInfo;
double value = (double)prop.GetValue(values);
return value;
}
}
public class CalculationOutput
{
public double property1 { get; set; }
public double property2 { get; set; }
public double property3 { get; set; }
public double property4 { get; set; }
}
public enum ComparisonTypes
{
GreaterThan,
Equals
}
The one gotcha of this method, is that you need to create an instance of the object whilst building up the Lines property, otherwise you can't actually access the property through the lambda.
If you only need this for a single class, then this is likely overkill, but it will work with essentially any class.

c# how to retrive objects as values from array

I am trying to create a simple 'inventory' system that stores items with the key being an items name, and with the remaining information being stored as a value. However, I am having difficulty figuring out how to then read the information. For example, if I have say a list of 10 items, and I want to select the items 'type' information from the key 'television' outlined below, how could I do this?
television {large, 5, 3, false, dynamic, 0.8, 20}
Hashtable myItems = new Hashtable();
protected virtual bool OnAttempt_AddItem(object args) {
object[] arr = (object[])args;
string ItemType = (string)arr[0];
string ItemName = (string)arr[1];
int ItemAmount = (arr.Length == 2) ? (int)arr[2] : 1;
int ItemACanHave = (arr.Length == 3) ? (int)arr[3] : 1;
bool ItemClear = (bool)arr[4];
string ItemEffect = (string)arr[5];
float ItemModifier = (float)arr[6];
int ItemWeight = (int)arr[7];
// enforce ability to have atleast 1 item of each type
ItemACanHave = Mathf.Max(1, ItemACanHave);
myItems[ItemName] = new object[] {ItemType, ItemAmount, ItemACanHave, ItemClear, ItemEffect, ItemModifier, ItemWeight };
return true;
}
Create an item class to encapsulate the properties:
public class InventoryItem
{
public string Name;
public string Type;
public int Amount;
public int CanHave; // you should consider renaming this - it's very unclear what this could mean
public bool Clear;
public string Effect;
public float Modifier;
public int Weight;
}
Then you can use a Dictionary to store items:
Dictionary<string, InventoryItem> inventory = new Dictionary<string, InventoryItem>();
inventory["television"] = new InventoryItem
{
Name = "television", Type = "large", Amount = 5,
CanHave = 3, Clear = false, Effect = "dynamic",
Modifier = 0.8, Weight = 20
});
And you can look it up like this:
Console.WriteLine("Type of television is: ", inventory["television"].Type);
I would suggest you to consider the possibility of more than one item of a certain type in a inventory list, i.e. two or more television sets instead of only one.
Use a base class and derived classes:
public class InventoryItem
{
public string ItemType { get; set; }
public string ItemName { get; set; }
public int ItemAmount { get; set; }
public int ItemACanHave { get; set; }
public bool ItemClear { get; set; }
public string ItemEffect { get; set; }
public float ItemModifier { get; set; }
public int ItemWeight { get; set; }
}
public class Radio : InventoryItem
{
}
public class Television : InventoryItem
{
}
// TODO: add your derived classes
Use a List<InventoryItem> to store the collection:
List<InventoryItem> InventoryItems = new List<InventoryItem>();
Modify your method (don't forget to add exception handling, as sometimes you might get different input than the one you expected in the args object):
protected virtual bool OnAttempt_AddItem(object args)
{
// TODO: handle unboxing exceptions, size of the array etc
//
try
{
object[] arr = (object[])args;
switch (arr[0].ToString().ToLower())
{
// TODO: add other types (Radio etc)
case "television":
var tv = new Television();
tv.ItemType = (string)arr[0];
tv.ItemName = (string)arr[1];
tv.ItemAmount = (arr.Length == 2) ? (int)arr[2] : 1;
tv.ItemACanHave = (arr.Length == 3) ? (int)arr[3] : 1;
tv.ItemClear = (bool)arr[4];
tv.ItemEffect = (string)arr[5];
tv.ItemModifier = (float)arr[6];
tv.ItemWeight = (int)arr[7];
// enforce ability to have atleast 1 item of each type
tv.ItemACanHave = Math.Max(1, tv.ItemACanHave);
InventoryItems.Add(tv);
break;
default:
var genericItem = new InventoryItem();
genericItem.ItemType = (string)arr[0];
genericItem.ItemName = (string)arr[1];
genericItem.ItemAmount = (arr.Length == 2) ? (int)arr[2] : 1;
genericItem.ItemACanHave = (arr.Length == 3) ? (int)arr[3] : 1;
genericItem.ItemClear = (bool)arr[4];
genericItem.ItemEffect = (string)arr[5];
genericItem.ItemModifier = (float)arr[6];
genericItem.ItemWeight = (int)arr[7];
// enforce ability to have atleast 1 item of each type
genericItem.ItemACanHave = Math.Max(1, genericItem.ItemACanHave);
InventoryItems.Add(genericItem);
break;
//handle other cases
}
return true;
}
catch (Exception ex)
{
// log the error
return false;
}
}
Retrieve the filtered items like this:
var largeTvType = inventory.InventoryItems.OfType<Television>()
// filter by type (or other criteria)
.Where(tv => tv.ItemType == "large")
// select only the property your interested in (in the case below
// it will be always "television" because that's part of the
// logic inside the OnAttempt_AddItem method's switch statement)
.Select(tv => tv.ItemType);
Still, as ChrisWue suggested in his answer, if you know that your inventory lists will be very large, I'd recommend you to use a Dictionary<string, InventoryItem>, the string key being a unique inventory item identifier. It will be faster.

Categories

Resources