Lambda expressions with properties from base class [closed] - c#

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I have several data classes and want to select an item from a list. We want to use a lambda expression to do so. But it seems not to work as expected when the select property is in the base class.
Base Class
public class BaseData
{
public bool isSelected;
public int distance;
}
Derived class
public class PlayerData: BaseData
{
public string nickname
}
Some Logic
public class SelectData
{
public PlayerData GetPlayer()
{
List<PlayerData> playerdata = new List<PlayerData>();
// this list gets its data from a JSON file and is populated as expected.
// now we want to select the player data for processing.
PlayerData player = playerdata.Find(x => x.isSelected);
// on this part we unexpected results, when i move the isSelected to the class PlayerData it works perfect but than it is not possible to write a generic extensions with these Data classes.
return player;
}
}
We want to use a extension something like
public static int AddToDropDown<T>(this Dropdown dropdown,
List<T> baseDataList,
string displayText,
string iconName,
bool isSelected) where T : new() {
enter code here --- add to dropdown and add the item to PlayerData or ...
}
There are no complier errors... what is wrong?

For me it works fine:
See this example here, probably there is something else causing the issue:
public void Main()
{
List<PlayerData> playerdata = new List<PlayerData>
{
new PlayerData
{
isSelected = true,
distance = 3,
nickname = "First",
},
new PlayerData
{
isSelected = true,
distance = 3,
nickname = "Second",
},
new PlayerData
{
isSelected = true,
distance = 3,
nickname = "Third",
}
};
PlayerData player = playerdata.Find(x => x.isSelected);
Console.WriteLine(player);
}
public class BaseData
{
public bool isSelected;
public int distance;
}
public class PlayerData: BaseData
{
public string nickname;
public override string ToString() { return this.nickname;}
}
The result is 'First' as expected since it is the first player in the list.

Related

Array of Objects to String - ASP.NET C# [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 months ago.
Improve this question
Trying to convert Array of Objects to String using C#. Able to achieve the same using LINQ, however trying to make use of reusable functions which would accept array of Objects and return back string. Understand that generics must be used but it's been hard time understanding it. Thanks in advance!
public class HelloWorld {
public static void Main() {
Root root = new Root();
List<A> obj = new List<A>();
obj.Add(new A() { Code = "WAY"});
obj.Add(new A() { Code = "DOWN"});
obj.Add(new A() { Code = "WE"});
obj.Add(new A() { Code = "GO"});
root.A = obj;
string _Result = string.Join("-", root.A.Where(x => x.Code != "").Select(p => p.Code.ToString()).ToArray());
Console.WriteLine(_Result); //Expected OP: WAY-DOWN-WE-GO
Console.WriteLine(Utility.ToArray(root.A)); //System.Collections.Generic.List`1[A]
}
//Trying for much simpler Generic function here.
public class Utility{
public static string ToArray(IList<Object> obj){
foreach(var v in obj){
//generic function..
}
StringBuilder sb = new StringBuilder();
return sb.ToString();
}
}
}
public class Root
{
public List<A> A { get; set; }
public List<B> B { get; set; }
}
public class A
{
public string Code { get; set; }
}
public class B
{
public string Mode { get; set; }
}
If you're looking for a system that works without having to have your classes implement an interface (for objects you didn't create, for example), it's possible to use a Func<T, object> to select a specific property/field:
// IEnumerable is more generic over "lists" (sets, maps, etc)
// \/
public static string ToArray<T>(IEnumerable<T> obj, Func<T, object> func) {
return string.Join('-', obj.Select(func));
}
Usage example:
List<A> aObjects = new();
//add aObjects
List<B> bObjects = new();
//add bObjects
Console.WriteLine(Utilities.ToArray(aObjects, a => a.Code));
Console.WriteLine(Utilities.ToArray(bObjects, b => b.Mode));
Reference: How to join as a string a property of a class?

Why does instantiating a new object in each iteration of a for loop overwrite previous list entries? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I'm trying to dynamically populate a list of objects as follows:
using System;
using System.Collections.Generic;
namespace test
{
class MainClass
{
public static void Main(string[] args)
{
List<string> items = new List<string>() { "item1", "item2", "item3" };
List<MainObject> objects = new List<MainObject>();
foreach (var item in items)
{
objects.Add(new MainObject(item));
foreach (MainObject o in objects)
Console.WriteLine(o.getString());
}
}
}
class MainObject
{
public MainObject() { }
public MainObject(string theString) { nString = theString; cString = theString; }
private static string nString { get; set; }
private static string cString { get; set; }
public string getString() { return nString; }
}
}
I figure that since I'm using the "new" operator I'm instantiating a new object each loop, but it overwrites the previous entry every time.
It gives an output like the following:
item1
item2
item2
item3
item3
item3
It is side effect of the way how you use WriteLine, or, to be more precise, where you use WriteLine.
Modify your code to something like this:
List<Object> objects = new List<Object>();
foreach (var theString in stringList)
{
objects.Add(new Object(theString));
}
//this should be outside of first loop
foreach (Object object in objects)
{
Console.WriteLine(object.getValue());
}
The definition of MainClass is using static fields and that's causing their values to be overwritten each time a new MainObject is created.
There's only one memory location for each static field. In the constructor, that memory location is being assigned the value of the parameter, which overwrites any value previously passed to the constructor.
On the other hand, an instance (non-static) field has a separate location within each instance. In that case, the assignment affects that object instance and not any others.
All you have to do is remove the static modifier from those properties.
class MainObject
{
public MainObject() { }
public MainObject(string theString) { nString = theString; cString = theString; }
// Remove 'static' from these two declaration
private string nString { get; set; }
private string cString { get; set; }
public string getString() { return nString; }
}

How to access a subclass using a string variable

I have a stucture of classes and subclasses as follows:
public class Regions
{
public const string UNITEDSTATES = "United States";
public static string[] members = { UNITEDSTATES};
public static class UnitedStatesTypes
{
public const string STEEL = "steel";
public const string CONCRETE = "concrete";
public static string[] members = { STEEL, CONCRETE };
public static class SteelStandards
{
public const string A36 = "ASTM A36";
public static string[] members = { A36 };
public static class A36Grades
{
public const string GRADE_36 = "Grade 36";
public static string[] members = { GRADE_36 };
}
public static class ConcreteStandards
{
...
}
There are more values under each one of the classes, but this is just a small sample so you can get the idea of what it looks like. I am trying to create a UI to select each one of these. There are 4 dropdown menus, each menu is populated by the value of the higher menu. So if the standards dropdown is on SteelStandards, the next dropdown is populated with A36, if it was on ConcreteStandards the next would be populated with the data under ConcreteStandards. Is there a way that I can access a subclass using a string variable?
For example, the first dropdown will select United States. The next dropdown needs to piece together "UnitedStatesTypes" and then access Regions.UnitedStatesTypes.members. I have tried using braces
Regions["UnitedStatesTypes"].members
but this did not work. Is there a way to make this happen? Or is there a better way to organize my data?
You could do this with just dictionaries, albeit it gets a bit unwieldy as you go down the tree:
var regions = new Dictionary<string,Dictionary<string,Dictionary<string,Dictionary<string,List<string>>>>>();
// populate it. Yes I know how ugly that will look!
var usSteelStandards = regions["United States"]["Steel"]["Standards"];
A better way might be to refactor your code as a set of class instances, instead of trying to use static classes/members all the way. It is a typical tree structure
public class Node : IEnumerable<Node>
{
public Node(string text)
{
this.Text = text;
this.Children = new List<Node>();
}
public string Text {get; private set;}
public List<Node> Children { get; private set;}
public Node this[string childText]
{
get{ return this.Children.FirstOrDefault(x => x.Text == childText); }
}
public void Add(string text, params Node[] childNodes)
{
var node = new Node(text);
node.Children.AddRange(childNodes);
this.Children.Add(node);
}
public IEnumerator<Node> GetEnumerator()
{
return Children.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
This can then be setup and used much easier
var node = new Node("root")
{
{
"United States",
new Node("Steel")
{
{
"ASTM A36",
new Node("Grade 36")
}
},
new Node("Concrete")
{
}
}
};
Console.WriteLine(node["United States"].Children.Count);
Console.WriteLine(node["United States"]["Steel"]["ASTM A36"].Children[0].Text);
Live example: https://rextester.com/QVGN99585

Why is my game serializing this class?

So I'm making a game, and it saves users' progress on the computer in a binary file. The User class stores a few things:
Integers for stat values (Serializable)
Strings for the Username and the skin assets
Lists of both the Achievement class and the InventoryItem class, which I have created myself.
Here are the User fields:
public string Username = "";
// ID is used for local identification, as usernames can be changed.
public int ID;
public int Coins = 0;
public List<Achievement> AchievementsCompleted = new List<Achievement>();
public List<InventoryItem> Inventory = new List<InventoryItem>();
public List<string> Skins = new List<string>();
public string CurrentSkinAsset { get; set; }
The Achievement class stores ints, bools, and strings, which are all serializable. The InventoryItem class stores its name (a string) and an InventoryAction, which is a delegate that is called when the item is used.
These are the Achievement class's fields:
public int ID = 0;
public string Name = "";
public bool Earned = false;
public string Description = "";
public string Image;
public AchievmentDifficulty Difficulty;
public int CoinsOnCompletion = 0;
public AchievementMethod OnCompletion;
public AchievementCriteria CompletionCriteria;
public bool Completed = false;
And here are the fields for the InventoryItem class:
InventoryAction actionWhenUsed;
public string Name;
public string AssetName;
The source of the InventoryAction variables are in my XNAGame class. What I mean by this is that the XNAGame class has a method called "UseSword()" or whatever, which it passes into the InventoryItem class. Previously, the methods were stored in the Game1 class, but the Game class, which Game1 inherits from, is not serializable, and there's no way for me to control that. This is why I have an XNAGame class.
I get an error when trying to serialize: "The 'SpriteFont' class is not marked as serializable", or something like that. Well, there is a SpriteFont object in my XNAGame class, and some quick tests showed that this is the source of the issue. Well, I have no control over whether or not the SpriteFont class is Serializable.
Why is the game doing this? Why must all the fields in the XNAGame class be serializable, when all I need is a few methods?
Keep in mind when answering that I'm 13, and may not understand all the terms you're using. If you need any code samples, I'll be glad to provide them for you. Thanks in advance!
EDIT: One solution I have thought of is to store the InventoryAction delegates in a Dictionary, except that this will be a pain and isn't very good programming practice. If this is the only way, I'll accept it, though (Honestly at this point I think this is the best solution).
EDIT 2: Here's the code for the User.Serialize method (I know what I'm doing in inefficient, and I should use a database, blah, blah, blah. I'm fine with what I'm doing now, so bear with me.):
FileStream fileStream = null;
List<User> users;
BinaryFormatter binaryFormatter = new BinaryFormatter();
try
{
if (File.Exists(FILE_PATH) && !IsFileLocked(FILE_PATH))
{
fileStream = File.Open(FILE_PATH, FileMode.Open);
users = (List<User>)binaryFormatter.Deserialize(fileStream);
}
else
{
fileStream = File.Create(FILE_PATH);
users = new List<User>();
}
for (int i = 0; i < users.Count; i++)
{
if (users[i].ID == this.ID)
{
users.Remove(users[i]);
}
}
foreach (Achievement a in AchievementsCompleted)
{
if (a.CompletionCriteria != null)
{
a.CompletionCriteria = null;
}
if (a.OnCompletion != null)
{
a.OnCompletion = null;
}
}
users.Add(this);
fileStream.Position = 0;
binaryFormatter.Serialize(fileStream, users);
You cannot serialize a SpriteFont by design, actually this is possible (.XNB file) but it hasn't been made public.
Solution:
Strip it off your serialized class.
Alternatives:
If for some reasons you must serialize some font, the first thing that comes to my mind would be to roll-out your own font system such as BMFont but that's a daunting task since you'll have to use it everywhere else where you might already do ...
Generate a pre-defined amount of fonts (i.e. Arial/Times/Courier at size 10/11/12 etc ...) using XNA Content app (can't recall its exact name); then store this user preference as two strings. With a string.Format(...) you should be able to load the right font back quite easily.
Alternative 2 is certainly the easiest and won't take more than a few minutes to roll-out.
EDIT
Basically, instead of saving a delegate I do the following:
inventory items have their own type
each type name is de/serialized accordingly
their logic does not happen in the main game class anymore
you don't have to manually match item type / action method
So while you'll end up with more classes, you have concerns separated and you can keep your main loop clean and relatively generic.
Code:
public static class Demo
{
public static void DemoCode()
{
// create new profile
var profile = new UserProfile
{
Name = "Bill",
Gold = 1000000,
Achievements = new List<Achievement>(new[]
{
Achievement.Warrior
}),
Inventory = new Inventory(new[]
{
new FireSpell()
})
};
// save it
using (var stream = File.Create("profile.bin"))
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, profile);
}
// load it
using (var stream = File.OpenRead("profile.bin"))
{
var formatter = new BinaryFormatter();
var deserialize = formatter.Deserialize(stream);
var userProfile = (UserProfile) deserialize;
// set everything on fire :)
var fireSpell = userProfile.Inventory.Items.OfType<FireSpell>().FirstOrDefault();
if (fireSpell != null) fireSpell.Execute("whatever");
}
}
}
[Serializable]
public sealed class UserProfile
{
public string Name { get; set; }
public int Gold { get; set; }
public List<Achievement> Achievements { get; set; }
public Inventory Inventory { get; set; }
}
public enum Achievement
{
Warrior
}
[Serializable]
public sealed class Inventory : ISerializable
{
public Inventory() // for serialization
{
}
public Inventory(SerializationInfo info, StreamingContext context) // for serialization
{
var value = (string) info.GetValue("Items", typeof(string));
var strings = value.Split(';');
var items = strings.Select(s =>
{
var type = Type.GetType(s);
if (type == null) throw new ArgumentNullException(nameof(type));
var instance = Activator.CreateInstance(type);
var item = instance as InventoryItem;
return item;
}).ToArray();
Items = new List<InventoryItem>(items);
}
public Inventory(IEnumerable<InventoryItem> items)
{
if (items == null) throw new ArgumentNullException(nameof(items));
Items = new List<InventoryItem>(items);
}
public List<InventoryItem> Items { get; }
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
var strings = Items.Select(s => s.GetType().AssemblyQualifiedName).ToArray();
var value = string.Join(";", strings);
info.AddValue("Items", value);
}
#endregion
}
public abstract class InventoryItem
{
public abstract void Execute(params object[] objects);
}
public abstract class Spell : InventoryItem
{
}
public sealed class FireSpell : Spell
{
public override void Execute(params object[] objects)
{
// using 'params object[]' a simple and generic way to pass things if any, i.e.
// var world = objects[0];
// var strength = objects[1];
// now do something with these !
}
}
Okay, so I figured it out.
The best solution was to use a Dictionary in the XNAGame class, which stores two things: an ItemType (an enumeration), and an InventoryAction. Basically, when I use an item, I check it's type and then look up it's method. Thanks to everyone who tried, and I'm sorry if the question was confusing.

Display List contents specific field in a ComboBox (C#)

Well, my question is a bit silly, but I've tried lots of different thing without result.
I have a ComboBox in my main form and I want to point its data source to the public readonly List PriceChanges list declared in the Filters class. No problem with that but I want to list the Description field.
I tried to assign the "Description" string to the DisplayMember attribute without success. My ComboBox only lists: "BusinessLogic.PriceChange" for each entry, where BusinessLogic is the name of my Namespace and PriceChange the class.
I appreciate any help.
Regards
That's part of the code of my main form
public mainFrm()
{
InitializeComponent();
prodFilter = new Filters();
cbPriceChanges.DataSource = prodFilter.PriceChanges;
cbPriceChanges.DisplayMember = "Description"
}
That's is part of the code that declares the List object
public enum PriceChangeTypes
{
No_Change,
Increased,
Decreased,
All
}
public class PriceChange
{
public String Description;
public readonly PriceChangeTypes Type;
public delegate bool ComparisonFuntionDelegate(Decimal a);
public readonly ComparisonFuntionDelegate ComparisonFunction;
public PriceChange(String Description, PriceChangeTypes type , ComparisonFuntionDelegate CompFunc)
{
this.Description = Description;
Type = type;
ComparisonFunction = CompFunc;
}
}
public class Filters
{
public readonly List<PriceChange> PriceChanges = null;
public Filters()
{
PriceChanges = new List<PriceChange>();
PriceChanges.Add(new PriceChange("No Change", PriceChangeTypes.No_Change, PriceChange => PriceChange == 0));
PriceChanges.Add(new PriceChange("Increased", PriceChangeTypes.Increased, PriceChange => PriceChange > 0));
PriceChanges.Add(new PriceChange("Decreased", PriceChangeTypes.Decreased, PriceChange => PriceChange < 0));
PriceChanges.Add(new PriceChange("All", PriceChangeTypes.All, a => true));
}
}
Have you tried making "Description" a property? It will change a lot in case the list tries to get the field through reflection (as it most likely does).
public class PriceChange {
public string Description{
get;
set;
}
// ...
}
Try adding this to your class :
public override string ToString()
{
return Description;
}
Currently you're just getting the default value of ToString, which is the object namespace and class

Categories

Resources