Identify derived class and instance in one way - c#

I don't really find a good title for this question but it goes as follows. I find myself in a situation where I want to identify a class and an instance of the class. The solution I have is to use a const value for the class and a property that returns that const value for the instance.
I don't know if there is a better solution. It feels a bit strange that I need two ways to identify and just reuse the value. Is there a better way?
Pseudo code below. In the real application there will be more classes that derive from the base class and the objects list will contain instances of these as well. Deserialization happens at startup, serialization at shutdown after after the list has been altered due to user activity.
static void Main(string[] args)
{
List<Base> objects = new List<Base>();
List<string> serializationIds = new List<string>();
// SerializationIds initialized somehow
foreach (var serializationId in serializationIds)
{
switch(serializationId)
{
// Identify class
case Derived.SerializationIdText:
objects.Add(new Derived());
break;
}
}
// add remove objects
foreach (var item in objects)
{
// Identify instance
string serializationId = item.SerializationId;
// Do something with serializationId;
}
}
public abstract class Base
{
public string SerializationId { get; set; }
}
public class Derived : Base
{
public const string SerializationIdText = "DatabaseExplorer";
public Derived()
{
SerializationId = SerializationIdText;
}
}

Instead of looping twice, why not perform the functions of the second loop withint the first the first?
static void Main(string[] args)
{
List<Base> objects = new List<Base>();
List<string> serializationIds = new List<string>();
// SerializationIds initialized somehow
foreach (var serializationId in serializationIds)
{
switch(serializationId)
{
// Identify class
case Derived.SerializationIdText:
string serializationId = item.SerializationId;
// Do something with serializationId;
break;
}
}
}
You might be able to refactor out the code within the switch statement, too, so you could have something like
static void Main(string[] args)
{
List<Base> objects = new List<Base>();
List<string> serializationIds = new List<string>();
// SerializationIds initialized somehow
foreach (var serializationId in serializationIds)
{
string serializationId = item.SerializationId;
// Do something with serializationId;
}
}

Related

Initialize get-only collection in object initializer from existing collection

I have a class with a get-only collection property. I would like to initialize the collection with the values from an existing collection.
I know that it is possible to initialize the collection using a collection initializer. I could also create the object and then use AddRange on the collection to add the items of the existing collection. This would however create the object with an empty list and add the existing items afterwards.
Is there a way to create the object with the List properly initialized in the first place (without adding a constructor, of course)?
using System.Collections.Generic;
namespace EmptyConsoleApp
{
internal class Program
{
public static void Main(string[] args)
{
// Compiles, but is not what I need
var firstHolder = new Holder()
{
TheList = {"A", "B"}
};
// Compiles, but initializes the list after object creation
var existingList = new List<string>() {"Foo", "Bar"};
var secondHolder = new Holder();
secondHolder.TheList.AddRange(existingList);
// Does not compile
var thirdHolder = new Holder()
{
TheList = {existingList}
};
}
}
internal class Holder
{
public Holder()
{
TheList = new List<string>();
}
public List<string> TheList { get; }
}
}
No. You can't assign this read-only property from a collection initializer. It is read-only after all.
TheList = { "A", "B" } works since it calls Add on TheList (once for each item added), it doesn't create and assign a new instance, which it is not allowed to.
TheList = { existingList } doesn't work since there is a typing issue (TheList = { existingList[0] } does work).
The best option you have it to create a constructor parameter and drop your idea of using collection initializers for something it isn't fit for.
Is there a way to create the object with the List properly initialized in the first place (without adding a constructor, of course)?
No
It's not. That's what a constructor does. If you don't want to do it in the constructor, there is no way to do it.
it is not possible to initialize a read only property from outside of the class itself.
collection initializer is just a simplified syntax version and it does not mean using this syntax you have the same access as if you are in the class constructor
thirdHolder.TheList = existingList; // this is the traditional way
Perhaps you can use factory class pattern like this
internal class Program
{
public static void Main(string[] args)
{
// Compiles, but is not what I need
var firstHolder = new Holder()
{
TheList = { "A", "B" }
};
// Compiles, but initializes the list after object creation
var existingList = new List<string>() { "Foo", "Bar" };
var secondHolder = new Holder();
secondHolder.TheList.AddRange(existingList);
// Does not compile
//var thirdHolder = new Holder()
//{
// TheList = existingList
//};
//thirdHolder.TheList = existingList; // this is the traditional way
var thirdHolder = Holder.HolderFactory(existingList);
}
}
internal class Holder
{
public Holder()
{
TheList = new List<string>();
}
public static Holder HolderFactory(List<string> theList)
{
return new Holder(theList);
}
private Holder(List<string> theList)
{
this.TheList = theList;
}
public List<string> TheList { get; }
}

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.

How to Create and Bind the Grid for Display With Quintuple Nested Lists C# ASP.net .aspx

I'm working on a project where it is necessary for me to create and bind the grid to display a response from a server which is a list of classes and some fields, the lists of classes also contain some variables as well as another list of a different class which contains some fields and another list of a different class...and goes on for five levels.
I must display the top level class and all of the lists of classes as well as each of the nested lists within the list and by itself. Allow me to use pseudocode to try to better explain with a triple tier. I am dealing with a quintuple tier.
classA
{
List<classB> classBList;
List<classC> classCList;
int whatever;
string something;
}
ClassB
{
List<classC> classCList;
int somethingElse;
string otherThing;
}
classC
{
int somethingA;
string somethingB;
}
List<ClassA> list1;
I am trying to create and bind and display the grid for list1. I've mainly been a straight back end coder so the .aspx page is what is really throwing me for a loop. I've figured out how bind and display with fields and fields within classes and a single list, but these lists are really challenging for me and I haven't made any progress in a couple of days.
Any help is much appreciated!
Maybe something like where you flatten all the values down and use a dictionary to track their origin (if it's needed). You would then have a flattened list of all values, and could easily create or recreate an original list of values (those belonging to ClassA.ClassBList, for example) using Linq or the Dictionary keys themselves:
public class flattenedList
{
public string whatever;
public int whateverInt;
}
public class nested
{
private Dictionary<string, List<flattenedList>> listData = new Dictionary<string, List<flattenedList>>();
private List<ClassA> list1 = new List<ClassA>();
ClassA classA = new ClassA();
ClassB classB = new ClassB();
ClassC classC = new ClassC();
public void processCalsses()
{
string key = "";
foreach (ClassA a in list1)
{
key = "ClassA.ClassBList";
foreach (ClassB b in classA.classBList)
{
addToDictionary(key, new flattenedList() { whatever = b.otherThing, whateverInt = b.somethingElse });
}
key = "ClassA.ClassCList";
foreach (ClassC c in classA.classCList)
{
addToDictionary(key, new flattenedList() { whatever = c.somethingB, whateverInt = c.somethingA });
}
addToDictionary("ClassA", new flattenedList() { whatever = a.something, whateverInt = a.whatever });
}
key = "ClassB.ClassCList";
foreach (ClassC c in classB.classCList)
{
addToDictionary(key, new flattenedList() { whatever = c.somethingB, whateverInt = c.somethingA });
}
addToDictionary("ClassB", new flattenedList() { whatever = classB.otherThing, whateverInt = classB.somethingElse });
addToDictionary("ClassC", new flattenedList() { whatever = classC.somethingB, whateverInt = classC.somethingA });
foreach (KeyValuePair<string, List<flattenedList>> kvp in listData)
{
for (int i = 0; i < kvp.Value.Count; i++)
{
Console.WriteLine(key + "[" + i.ToString() + "] whatever = " + kvp.Value[i].whatever);
Console.WriteLine(key + "[" + i.ToString() + "] whateverInt = " + kvp.Value[i].whateverInt.ToString() + "\n");
}
}
}
private void addToDictionary(string key, flattenedList f)
{
if (!listData.ContainsKey(key))
{
listData.Add(key, new List<flattenedList>());
}
listData[key].Add(f);
}
public class ClassA
{
public List<ClassB> classBList = new List<ClassB>();
public List<ClassC> classCList;
public int whatever;
public string something;
}
public class ClassB
{
public List<ClassC> classCList;
public int somethingElse;
public string otherThing;
}
public class ClassC
{
public int somethingA;
public string somethingB;
}
}
Note that I had to instantiate the classes in order to get the intellisense to let me type. I assume you'd create instance versions visible to this method either within class scope or within a public static class.
PS - if you don't need to maintain the origin of each datum (which class and list it came from), then there's an easier way to do this.

C# nested object properties

Hello I am doing some tests in C# with nesting properties which return objects, but I am getting an object reference exception.
I want to be able to access the arrays in nested Properties, but in the current context I can see that I'm not instancing any new objects inside the properties.
This is where the basic question comes up... Where do I declare a 'new' object instance in the middle of all this? Do I even need to declare and new object reference inside the 'foo' class or 'bar' class?
namespace CustomProperties_TEST
{
class Program
{
public foo[] Blatherskite { get; set; }
static void Main(string[] args)
{
Program myProgram = new Program();
myProgram.Blatherskite[0].CustomProperty1[0].CustomProperty2 = 999999999;
myProgram.Blatherskite[1].CustomProperty1[0].CustomProperty2 = 999999999;
foreach (var item in myProgram.Blatherskite)
{
Console.WriteLine(item.CustomProperty1[0].CustomProperty2);
}
}
}
class foo
{
private bar[] customevariable1;
public bar[] CustomProperty1
{
get { return customevariable1; }
set { customevariable1 = value; }
}
}
class bar
{
private int customintvariable2;
public int CustomProperty2
{
get { return customintvariable2; }
set { customintvariable2 = value; }
}
}
}
You would want to do something like the following, since arrays are initialized to null by default.
static void Main(string[] args)
{
Program myProgram = new Program();
// This is your missing initialization
myProgram.Blatherskite = new foo[2] {
new foo{CustomProperty1 = new bar[2]{new bar{CustomProperty2 = 1},new bar{CustomProperty2 = 2}}}
, new foo{CustomProperty1 = new bar[2]{new bar{CustomProperty2 = 3},new bar{CustomProperty2 = 4}}}};
myProgram.Blatherskite[0].CustomProperty1[0].CustomProperty2 = 999999999;
myProgram.Blatherskite[1].CustomProperty1[0].CustomProperty2 = 999999999;
foreach (var item in myProgram.Blatherskite)
{
Console.WriteLine(item.CustomProperty1[0].CustomProperty2);
}
}
Using arrays means you'll have to set their size. If you would want more flexibility, use a List, and then you can simply add items to it.

Change the reference of an instance in List

I have a question about assignment.
public class A {}
public class AHolder
{
public A AnInstance;
}
void Change()
{
A anotherInstance=new A();
anotherInstance.aField="bla";
A anotherInstance2=new A();
anotherInstance2.aField="blabla";
List<AHolder> aList= new List<AHolder>();
aList.add(new AHolder(){AnInstance=anotherInstance});
aList.add(new AHolder(){AnInstance=anotherInstance});
aList.add(new AHolder(){AnInstance=anotherInstance});
anotherInstance=anotherInstance2;
}
How can I implement the code that ensures the changes of all AnInstance values in aList, when anotherInstance changed without using loop?
Update:after executing the code lines above ,i'm trying to get "blabla" value from aList[0].AnInstance.aField.Is it possible?
You could do it using a wrapper class instance, instead of referencing directly to the AHolder instance, but think if you really need this extra indirection layer, as it would make your code less readable.
I expect the following sample explains how to do it:
public class MyData { public string Value; }
public class MyRef { public MyData Instance; }
void Change()
{
var dataFoo = new MyData() { Value = "foo" }
var dataBar = new MyData() { Value = "bar" }
var referer = new MyRef() { Instance = dataFoo }
var list= new List<MyRef>();
list.add(referer);
list.add(referer);
list.add(referer);
// for i=0 to 2 -> list[i].Instance.Value = "foo"
referer.Instance = dataBar;
// for i=0 to 2 -> list[i].Instance.Value = "bar"
}

Categories

Resources