How to create an array of the same properties? - c#

So pretty much, what I'm trying to do is create a properties grid. It will hold things like input 0-5 or like output 1-64, which will have sub-properties like name, id, etc. Right now my code is very straight forward, and I initialize each one individually. That caused a problem when I wanted to save them to a text file and realized that doing it that way will cause walls of code. Instead of just being able to do a for loop, I would have to grab each one individually to write to the text file. I was wondering if there was a better approach to doing this with an array or list.
Here is what I have right now:
[CategoryAttribute("Input 0"), DescriptionAttribute("Name of Input 0"), DisplayName("Input 0: Name")]
public string IName_0
{
get {return _Iname[0];}
set {_Iname[0] = value;}
}
[CategoryAttribute("Input 0"), DescriptionAttribute("ID of Input 0"), DisplayName("Input 0: ID")]
public int IID_0
{
get { return _IID[0]; }
set { if ((64 > value) && (value >= 0)) _IID[0] = value; }
}
[CategoryAttribute("Input 1"), DescriptionAttribute("Name of Input 1"), DisplayName("Input 1: Name")]
public string IName_1
{
get { return _Iname[1]; }
set { _Iname[1] = value; }
}
[CategoryAttribute("Input 1"), DescriptionAttribute("ID of Input 1"), DisplayName("Input 1: ID")]
public int IID_1
{
get { return _IID[1]; }
set { if ((64 > value) && (value >= 0)) _IID[1] = value; }
It goes on like that for each input. I have been looking everywhere, and I can't find a good fix.

First you have to realize that containing your properties in a collection will cause them to be grouped together under it, when displayed on your PropertyGrid.
Therefore, having a collection of strings and another of ints will break your current structure, of:
Having pairs of properties (name and ID) grouped together;
Having all the properties on the root level of the property grid.
Having said that, you could solve issue #1 by making #2 a bit worse.
create two classes:
MyClass - containing a Name and IID properties.
MyClassCollection - a container class (most likely using an underlying List).
To mitigate issue #2, you can add code to expand all nodes at initialization as shown here.
If you try this, you will notice that the "containers" (MyClassCollection and MyClass) will have some undesired decription text appearing next to them when displayed.
This is where this article comes in handy.
Your property would then be:
[DisplayName("Some info here"),
DescriptionAttribute("Some more descriptive info here...")]
virtual public MyClassCollection MyData { get; set; }
and your class definitions would be along the lines of:
public class MyClass
{
public string Name
{
get { return _name; }
set { _name = value; }
}
public int IID
{
get { return _iid; }
set { if ((64 > value) && (value >= 0)) _iid = value; }
}
private string _name;
private int _iid;
}
public class MyClassCollection : CollectionBase
{
// See the article for code for the overrides (for CollectionBase) and implementation (for ICustomTypeDescriptor)
}
If you look at the article mentioned above, regarding the tweaking of the display text of the container classes, you'll have to make some adjustments to the classes above.

Related

How to create a base class/struct for value objects that validates based on super class values?

this is a problem I'm not sure how to call it or how name the things I want to do but hopefully the code examples can speak for themselves. I come from a PHP background and am learning .NET Core 2.2/3.0. Something I'm running into right now though is how I can avoid some code duplication when creating similar classes. For example I want to create multiple value objects that basically just contain strings, but each value object has different constraints. In most cases however the only constraint differences are in the length.
So in PHP I would do something like this:
abstract class Text
{
abstract public static function maxCharacters(): int;
protected $value;
public function __construct(string $text)
{
if (mb_strlen($text) > static::maxCharacters()) {
throw new LengthException(sprintf('Too many characters, the max is %d.', static::maxCharacters()));
}
$this->value = $text;
}
public function value(): string
{
return $this->value;
}
}
class Name extends Text
{
public static function maxCharacters(): int
{
return 50;
}
}
class Description extends Text
{
public static function maxCharacters(): int
{
return 1000;
}
}
It's not the best example of inheritance, but the goal is to illustrate the point in that I would like to have 1 place where I can put my validation logic, and then in the subclass only have to define the parameters of the validation and not the actual logic to perform the validation.
Since we're dealing with value objects here I assume that in C# it would be best to use a (readonly) struct. So, without any inheritance, what I ended up with as the C# equivalent is:
public readonly struct Name
{
private const int MAX_CHARACTERS = 50;
public string Value
{
get { return this.Value; }
set
{
if (value.Length > Name.MAX_CHARACTERS)
{
throw new ArgumentOutOfRangeException(String.Format("Too many characters, the max is {0}.", Name.MAX_CHARACTERS));
}
this.Value = value;
}
}
public Name(string name) => this.Value = name;
}
public readonly struct Description
{
private const int MAX_CHARACTERS = 1000;
public string Value
{
get { return this.Value; }
set
{
if (value.Length > Description.MAX_CHARACTERS)
{
throw new ArgumentOutOfRangeException(String.Format("Too many characters, the max is {0}.", Description.MAX_CHARACTERS));
}
this.Value = value;
}
}
public Description(string description) => this.Value = description;
}
But as you can see without inheritance that's a lot of copy/paste, and copy/paste is something I prefer to avoid. The only alternative I could think of is to create a separate TextValidator class or something that I can call from the set to which I would only have to pass the max number of characters, but that would still leave me with more copy/paste than I'd prefer.
How would you write something like this in C# with as little copy/paste as possible?
It seems like you've got the basics of inheritance understood in your PHP example; I'd just suggest doing a bit of reading on the syntax in C#.
For what it's worth, the following would do the trick:
public abstract class Text{
public string Value { get; }
public Text(string val) {
if (val.Length > MAX) throw new Exception();
Value = val;
}
protected abstract int MAX{get;}
}
public class Name : Text{
public Name(string val): base(val) { }
protected override int MAX => 50;
}
public class Description : Text
{
public Description(string val) : base(val) { }
protected override int MAX => 1000;
}
I'll also add a footnote to say be careful calling abstract methods/properties from a class constructor (which is what I'm doing here). If, for example, the value of MAX is a computed value which depends on the object already having been initialized, you could run into some issues. In this case though, it won't be a problem.

get set property usage

I am a bit confused with the get set property in C#.
I have the simple code below:
using System;
class Example
{
int _number;
public int Number
{
get
{
return this._number;
}
set
{
this._number = value;
}
}
}
class Program
{
static void Main()
{
Example example = new Example();
example.Number = 5; // set { }
Console.WriteLine(example.Number); // get { }
}
}
The code above using get set properties. However, if I delete the get set code like below code, the results stay the same.
using System;
class Example
{
int _number;
public int Number;
{
}
}
class Program
{
static void Main()
{
Example example = new Example();
example.Number = 5; // set { }
Console.WriteLine(example.Number); // get { }
}
}
My query is, what is the get set code used for? In the above program, the results are same. Can you give me some simple code which show the get set usage?
In your code, Number is simply a public field, as evidenced by the semicolon (;) at the end.
public int Number;
It is not a property, you just have an empty set of brackets right underneath which led to your confusion. If you were to remove the ; then you would actually have a property that is missing it's get, and would not compile at all.
All properties need to have a getter (setters are optional). If you want to avoid writing them, you can use auto properties, which take care of the backing field without you having to get involved:
public int Number { get; set; } // No field required
Note: A common usage pattern you'll see involving auto properties is the following:
public int Number { get; private set; }
This allows for properties that can be read from anywhere, but can only be modified from within the class they belong to.
EDIT: To answer your question, the main difference between fields and properties is in encapsulation. You can read more about the general differences between fields and properties here.
However, the example you have given has one additional difference, the private set. A normal field can be written from and to throughout the program. A property with a private setter however can only be modified from inside the class it belongs to.
Example:
public class Foo
{
public int Id { get; private set; }
public string Name;
public Foo()
{
this.Id = 1; // This works!
}
}
Here, Name is a field and Id is a property with a private setter. Notice that we modify Id in the constructor and that works, because it is within the class Id belongs to. Moving outside the class however:
var foo = new Foo();
// Field (no get and set):
foo.Name = "test" // Works
string bar = foo.Name; // Works
// Property (get and *private* set)
int i = foo.Id; // Works, because get is public
foo.Id = 2; // Doesn't work, because set is private

Cut down on repeated structure of Properties

I have a class with many automatic string properties representing a customer. Briefly, something like this:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
This data comes from clients and many different file imports using a Nuget package. Those clients appear to be copying their data out of a database-generated system where we get literal "NULL" strings sometimes in some fields. I'd like to strip those out in an easy fashion with a little change to existing code as possible. I don't want errors thrown, I just want it to be null instead of "NULL" (not a validation failure for Address2, but is for LastName).
Because there are many imports and I'm using a library off Nuget without the source, I can't really catch the data coming in. All of it ends up in a pretty small number of classes before we process it, so this seems like the place to do it. I list two fields here, but there are over a dozen, and it's not just one class. We also use ValueInjector a lot, so swapping out string for a custom class isn't really an option.
The best I can come up with is undoing the auto-properties and using a string extension method for the null check. That just seems like a whole lot of repeated bloat. Is there any way to define this action on a property and say do that every time without turning one line of code into thirteen dozens of times over?
public static class StringExtensions
{
public static string NullMeansNull(this string value)
{
if (value == "NULL")
return null;
else
return value;
}
}
public class Person
{
private string _FirstName;
public string FirstName
{
get
{
return this._FirstName.NullMeansNull();
}
set
{
this._FirstName = value;
}
}
private string _LastName;
public string LastName
{
get
{
return this._LastName.NullMeansNull();
}
set
{
this._LastName = value;
}
}
}
Note I'm not talking about code formatting. We have enough StyleCop and CodeAnalysis rules that keep things expanded similar to above, which is good when it's code you actually want to read.
I gave up trying not to touch each import. I just made a generic extension method I call after importing data that looks for any editable string property, checking for "NULL" or empty string and converting it to null.
public static T CleanNullishStrings<T>(this T obj) where T : class
{
foreach (System.Reflection.PropertyInfo property in obj.GetType().GetProperties())
{
if (property.PropertyType == typeof(string) && property.CanRead && property.CanWrite)
{
string value = (string)property.GetMethod.Invoke(obj, null);
value = string.IsNullOrEmpty(value) || value.ToUpper() == "NULL" ? null : value;
property.SetMethod.Invoke(obj, new object[] { value });
}
}
return obj;
}
Since it generically returns the object passed, I can use it in projections:
IEnumerable<Person> = personList.Select(p => p.CleanNullishStrings());

Implementing indexing in C# property

For what i have been reading, with another Class I would be able to add the indexing to the property. But i am not managing to achieve the get/set of the "Option[x]" property of my custom "Poll" class.
public class Poll
{
//Constructor
public Poll() { }
//Properties
public string Title
{
get { return title; }
set { title = value; }
}
public Options Option { get; set; }
private string title;
}
public class Options
{
string[] option = { };
public string this[int i]
{
get { return option[i]; }
set { option[i] = value; }
}
}
When i try to add the first option to the poll, it says the object ("Options") has not being instantiated. And it does make sense. But I couldn't figure out where in Poll i would instantiate it.
So, can anyone explain me what am i doing wrong? Am i following the right direction? Or point to me further reading. For the solutions I have seen, this one seemed the most logical to me, but was only a small raw example, low on details.
I didn't want to follow the dictionary (Implementing indexing "operator" in on a class in C#) way, or "Option" property returning a List of strings.
Change:
public Poll() { }
To:
public Poll() { Option = new Options(); }
Also pay attention to "Wai Ha Lee" pointed out: "the indexer will always throw an IndexOutOfRangeException because the option array is always an empty array."
What he means is that you have to replace:
string[] option = { };
With:
string[] option = new string[X]; //X is Array size

Objects Retrieved from List<T> Appear to Be Copies, Not References

In my code, I have a class that maintains a number of lists. We'll focus on one of them for the moment, since it's the one that highlighted the problem.
internal List<Badge> Badges { get; private set; }
In the code, I add Badge instances to this list when an XML document is parsed. Later, I want to update the individual instances in the list so I can have the data written back out to XML. Because of the way the data's XML structure differs from the original file structure, there's some hocus-pocus involved, but that's largely mapped out. The surprise came when I attempted to update an item in the List<Badge>.
Specifically, the problematic code is here:
// Get the current badge from the loaded XML data, so we can update it.
var currentBadge = this.GameData.GetCurrentBadge();
I always get a valid badge back. The surprise, as I've come to find out, is that this simple test always fails:
var result = this.GameData.Badges.IndexOf(currentBadge);
result always evaluates to -1, indicating that the object doesn't exist in the collection. (EDIT: Updating the properties on currentBadge has no effect whatsoever on the contents of the matching item in this.GameData.Badges.) Which leads me to conclude that I'm getting a copy of my object back, and not a reference, as I would have expected.
For the inquisitive, the code to retrieve badges from the GameData class is included below. I have a sneaking suspicion that this is a documented behavior of generic lists, and that this is the first time I've stumbled across it. If so, I'm in for a very rude awakening. If it's not, I'd really like to know why my objects are coming back "disconnected" from their originals.
private Badge GetCurrentBadge()
{
var badgeItem = GetCurrentBadgeItem();
if (badgeItem != null)
{
return this.GameData.GetBadgeByText(badgeItem.Text);
}
return null;
}
private MenuOption GetCurrentBadgeItem()
{
if (!(this.currentItem is MenuOption &&
(this.currentItem as MenuOption).IsLocked))
{
return null;
}
MenuOption result = null;
var children = this.currentMenu.Children;
for (var n = children.Count - 1; n >= 0; n--)
{
var child = children[n] as MenuOption;
if (child == null || !child.IsLocked)
{
break;
}
if (!child.Text.StartsWith(" "))
{
result = child;
break;
}
}
return result;
}
UPDATE: Per request, GetBadgeByText, which comes from the GameData class.
internal Badge GetBadgeByText(string badgeText)
{
foreach (var badge in Badges)
{
if (badge.Text.ToLower() == badgeText.ToLower())
{
return badge;
}
}
return null;
// var b = (from l in Badges
// where l.Text.ToLower().StartsWith(badgeText.ToLower())
// select l).FirstOrDefault();
//return b;
}
As you can see, I've tried it both with and without Linq, just to eliminate that as the culprit. Changing the implementation had no noticable effect.
And for the record, all the objects in this application are CLASSES. No structs anywhere.
UPDATE #2: The Badge class.
internal class Badge
: GameDataItem
{
public Badge()
: base()
{
}
public string AuthId { get; set; }
public string Category { get; set; }
public string Description { get; set; }
public bool IsAccoladePower { get; set; }
public string RequiredBadges { get; set; }
public override string ToString()
{
return Text;
}
internal string ToXml()
{
var template = "<Badge value=\"{0}\" title=\"{1}\" category=\"{2}\" authid=\"{3}\" requires=\"{4}\" accolade=\"{5}\" description=\"{6}\" />";
return string.Format(template,
this.Value,
this.Text,
this.Category,
this.AuthId,
this.RequiredBadges,
this.IsAccoladePower,
this.Description);
}
}
And just in case someone asks for it, the base class:
internal class GameDataItem
{
private string _text;
public string Text
{
get
{
return this._text;
}
set
{
this._text = value.Replace("<", "<")
.Replace(">", ">")
.Replace("&", "&");
}
}
public string Value { get; set; }
public override string ToString()
{
return Text + "=\"" + Value + "\"";
}
}
Looks to me like this has something to do with MenuOption's implementation of Equals(object). The IndexOf() method of the List<> will use Equals(object) when deciding what to return.
Either:
You are putting a copy of the object in the list. (List<T> does not clone objects or do any other sort of trickery.)
Badge is a struct, not a class, which means that you don't actually hold references to it since it would be a value type.
There's some copying going on elsewhere in code you haven't pasted.
A generic List<T> does not copy objects. You add references to it, and the same references comes out - so there must be another problem in the code.
How is GetBadgeFromText implemented ? Does it read directly from the Badges List ?
Is this a web app ? If yes, does your List live between requests, or is it deserialized and serialized on each request (this could also be the problem).

Categories

Resources