I have a set of user controls on a page that are dynamically loaded based on condition to run a variety reports (the condition driver). Each control has one or more properties exposed that will be used to get data from my database query. Because the controls vary for each report I wrote a procedure to access the appropriate control's property by name so I can send it to the database query in the code behind (C#). I got it all setup to access the public property like this:
stringVal = userControl.Attributes[stringName].ToString();
and it is telling me that I need to new up an object. I don't understand how I need to access that property dynamically by string name. In my immediate window I can see the property I want; but, it is not an "Attribute" as control.Attributes.Count = 0. So, how do I need to set this up properly so I can access it by string name? Do I need to decorate the property with something?
Thank you in advance.
Below, I've written a wrapper class you can use to set/get public fields or properties of the wrapped class by string name.
First, let's look at how you could use it on a class that had the public fields or properties StartDate and SocialSecurityNumber.
// First create the instance we'll want to play with
MyUserControl myControlInstance = new MyUsercontrol();
// MyUserContol has two properties we'll be using
// DateTime StartDate
// int SocialSecurityNumber
// Now we're creating a map to facilitate access to the properties
// of "myControlInstance" using strings
PropertyMap<MyUserControl> map =
new PropertyMap<MyUserControl>(myControlInstance);
// Since the map is directed toward "myControlInstance"
// this line is equivalent to:
// myControlInstance.StartDate = Datetime.Now;
map.Set<DateTime>("StartDate", DateTime.Now);
// This line is equivalent to:
// ssn = myUsercontrol.SocialSecurityNumber;
int ssn = map.Get<int>("SocialSecurityNumber");
And now on to how it's implemented:
public class PropertyMap<T>
{
readonly T Instance;
public PropertyMap(T instance)
{
Instance = instance;
}
public U Get<U>(string PropertyName)
{
// Search through the type's properties for one with this name
// Properties are things with get/set accessors
PropertyInfo property = typeof(T).GetProperty(PropertyName);
if (property == null)
{
// if we couldn't find a property, look for a field.
// Fields are just member variables, but you can only
// manipulate public ones like this.
FieldInfo field = typeof(T).GetField(PropertyName);
if (field == null)
throw new Exception("Couldn't find a property/field named " + PropertyName);
return (U)field.GetValue(Instance);
}
return (U)property.GetValue(Instance, null);
}
public void Set<U>(string PropertyName, U value)
{
// Search through the type's properties for one with this name
PropertyInfo property = typeof(T).GetProperty(PropertyName);
if (property == null)
{
// if we couldn't find a property, look for a field.
FieldInfo field = typeof(T).GetField(PropertyName);
if (field == null)
throw new Exception("Couldn't find a property/field named " + PropertyName);
field.SetValue(Instance, value);
return;
}
property.SetValue(Instance, value, null);
}
}
You need to explore reflection. It relates to manipulating metadata of .NET types (classes, structs, enums, etc). However, if you're running your app in shared hosting with partial trust, you may not be able to have any kind of reflection code run at all (security restriction on the server). If this is the case, test on a small/quick example first (on your hosting), and then make decisions appropriately: whether to redesign your app or change hosts.
A reflection-related snippet that might be useful to you (paste this somewhere within the user control class, as this.GetType() matters:
var properties = this.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
foreach (var p in properties)
{
string propertyName = p.Name;
}
There are other ways to get the type; e.g. typeof(yourclassname); you can also reflect/get all types from given assembly (dll), and much more.
Related
I have to following in vb.net and am having a rough time converting to c#. The fieldlist is a class that has several properties however only 2 are relevant for the below procedure. The code to call the procedure looks like myClass.Fields("Test") = 123456. The converters all drop the fieldname from the property. If I add it back then I get The name 'value' does not exist in the current context.
VB.net - works fine in VB
Public WriteOnly Property Fields(ByVal fieldName As String) As Object
Set(ByVal value As Object)
If mvarFieldData Is Nothing Then mvarFieldData = New FieldList
If mvarFieldData(fieldName) Is Nothing Then
mvarFieldData.Add(fieldName, value)
Else
mvarFieldData(fieldName).FieldValue = value
End If
End Set
End Property
c# - I'm doing something wrong here.
public object Fields(string fieldName)
{
set {
if (mvarFieldData == null)
{mvarFieldData = new FieldList();}
if (mvarFieldData[fieldName] == null)
{
mvarFieldData.Add(fieldName, value);
} else {
mvarFieldData[fieldName].FieldValue = value;
}
}
}
c# Converters(telerik) provide the below
public object Fields {
set {
if (mvarFieldData == null)
mvarFieldData = new FieldList();
if (mvarFieldData(fieldName) == null) {
mvarFieldData.Add(fieldName, value);
} else {
mvarFieldData(fieldName).FieldValue = value;
}
}
}
What is the scope of mVarFieldData, and what is it's exact type? It seems like it is a Collection of some sort.
The c# code above is not compilable as you are trying to mix a method syntax with a property syntax.
property:
public object Fields{ get; set{mvarFieldData = value;} }
Method:
public object Fields(string fieldname, object val){ mvarFieldData[fieldname] = val;}
By the looks of the decision making going on in the VB.Net property, I would think a public method may suit the situation better. I normally just use property's when a very minimum amount of validation is needed when setting a encapsulated type member.
Hope this helps.
You actually have a couple problems here.
Problem 1: Properties don't take arguments.
C# properties can't be passed an argument/parameter the way you're passing fieldname. There are a couple different ways you could solve this:
You can make an index property (used with myObject[fieldName] = blah rather than myObject.Fields[fieldName] = blah). Use the syntax public object this[string fieldName] to declare the property.
Since your property doesn't have a getter, you can just turn it into a single method with signature public void SetField(string fieldName, object value), called like so: myObject.SetField(fieldName, value).
You can expose mvarFieldData through a getter property: public Dictionary<?, ?> Fields { get { return mvarFieldData; } } which will let users access the dictionary (I assume that's what it is, based on usage) directly.
Problem 2: The key may not exist.
I'm not sure about dictionaries (or whatever similar structure mvarFieldData is) in VB.NET, but in C# there's a difference between a key whose value is null and a key that's not present in the structure.
var dict = new Dictionary<int, string>();
dict.Add(1, null);
if (dict[1] == null)
Console.WriteLine("This line will be displayed.");
if (dict[2] == null)
Console.WriteLine("The line above this one will throw an exception.");
In addition to your present code, you need a check for mvarFieldData.ContainsKey(fieldName) before you start checking the value associated with fieldName.
I'm working on a business application that use the PropertyGrid. My project leader want me to localize the texts in the PropertyGrid at runtime. Hurray!!! irony
I have tried many days to localize the PropertyGrid. But I have trouble changing the attributes Description and Category at runtime. Changing the DisplayName works fine.
I have made a simple example to reproduce the issue: Create a Windows Form application and from the ToolBox add a PropertyGrid and a Button with default settings.
Here is the class I would like to display in the PropertyGrid:
class Person
{
int age;
public Person()
{
age = 10;
}
[Description("Person's age"), DisplayName("Age"), Category("Fact")]
public int Age
{
get { return age; }
}
}
In the Form's constructor; I create the Person object and display it in the PropertyGrid.
public Form1()
{
InitializeComponent();
propertyGrid1.SelectedObject = new Person();
}
The button is used to change the DisplayName, Description and Category attributes at runtime.
private void button1_Click(object sender, EventArgs e)
{
SetDisplayName();
SetDescription();
SetCategory();
propertyGrid1.SelectedObject = propertyGrid1.SelectedObject; // Reset the PropertyGrid
}
The SetDisplayName() method works fine and actually changes the DisplayName of the property in runtime!
private void SetDisplayName()
{
Person person = propertyGrid1.SelectedObject as Person;
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(person)["Age"];
DisplayNameAttribute attribute = descriptor.Attributes[typeof(DisplayNameAttribute)] as DisplayNameAttribute;
FieldInfo field = attribute.GetType().GetField("_displayName", BindingFlags.NonPublic | BindingFlags.Instance);
field.SetValue(attribute, "The age");
}
SetDescription() and SetCategory() methods are almost identical to the SetDisplayName() method, except for some type changes and strings to access the private member of each attributes.
private void SetDescription()
{
Person person = propertyGrid1.SelectedObject as Person;
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(person)["Age"];
DescriptionAttribute attribute = descriptor.Attributes[typeof(DescriptionAttribute)] as DescriptionAttribute;
FieldInfo field = attribute.GetType().GetField("description", BindingFlags.NonPublic |BindingFlags.Instance);
field.SetValue(attribute, "Age of the person");
}
private void SetCategory()
{
Person person = propertyGrid1.SelectedObject as Person;
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(person)["Age"];
CategoryAttribute attribute = descriptor.Attributes[typeof(CategoryAttribute)] as CategoryAttribute;
FieldInfo[] fields = attribute.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
FieldInfo field = attribute.GetType().GetField("categoryValue", BindingFlags.NonPublic | BindingFlags.Instance);
field.SetValue(attribute, "Info");
}
Both SetDescription() and SetCategory() methods compile and run but don't effext the ProperytGrid. After the last line of each method you can use the IntelliSense to see that the the Attribute object (DescriptionAttribute and CategoryAttribute) has a member that has changed.
After running these three methods and resetting the PropertyGrid (see button1 click method); the PropertyGrid has only changed the DisplayName attribute. The Description and the Category attributes are unchanged.
I would really like some help to solve this issue. Please any suggestion or solutions?
Note 1:
I don't want any responses saying that this is impossible and the attributes can only be set at design time. That is not true! This article from CodeProject.com show an example how to localize the PropertyGrid and to change the attributes in runtime. Unfortunately I have problem scoping the example for those parts I need to solve this issue.
Note 2:
I would like to avoid using resouce files. This is due to the localization is located in different language files. Each file contain a bunch of indices, each with a string value. All indices and string values are loaded into Dictionary object. To access a string the index is used to access it. I most, unfortunately, use this solution.
Best regards,
/Mc_Topaz
Here is a good article for Globalized-property-grid
You can give many resource file for the Person ,than will the propery-grid will localize.
Here is three step:
inherit from GlobalizedObject
Give the resource file for Person with the same name (eg.Person.zh-cn.resx)
change the thread's cuture which you want to display.
You can try ,Good Luck!
What you could do is reuse the DynamicTypeDescriptor class described in my answer to this question here on SO: PropertyGrid Browsable not found for entity framework created property, how to find it?
like this:
public Form1()
{
InitializeComponent();
Person p = new Person();
DynamicTypeDescriptor dt = new DynamicTypeDescriptor(typeof(Person));
propertyGrid1.SelectedObject = dt.FromComponent(p);
}
private void button1_Click(object sender, EventArgs e)
{
DynamicTypeDescriptor dt = (DynamicTypeDescriptor)propertyGrid1.SelectedObject;
DynamicTypeDescriptor.DynamicProperty dtp = (DynamicTypeDescriptor.DynamicProperty)dt.Properties["Age"];
dtp.SetDisplayName("The age");
dtp.SetDescription("Age of the person");
dtp.SetCategory("Info");
propertyGrid1.Refresh();
}
xudong125 answer solves the issue! I managed to work around the resource files solution by using a static source instead. It's to complicated to explain...
But creating classes implementing ICustomTypeDescriptor and PropertyDescriptor is the way to go.
The key was to override DisplayName, Description and Category methods in the sub class of the PropertyDescriptor class. In these overriden methods I pointed to a public static source and managed to get the strings I wanted.
/Mc_Topaz
I have a different reason to change the property description and found a fairly crude but much simpler solution for just correcting the description that is shown in the grid. Advantage for me was that the class of the object shown in the property grid required much less change.
My situation was the following: I have two boolean properties A and B, where B only can be used if A is set. If A is False, I want to make B read-only and set its description to something like "This property can only be used if you set 'A' to True". In the object code I set the Description attribute of B to this message, similar to how Mc_Topaz does that.
To only set the description that is shown for the selected property to its correct current value, I use the following SelectedGridItemChanged event handler for my PropertyGrid named pgConfig:
private void pgConfig_SelectedGridItemChanged(object sender, SelectedGridItemChangedEventArgs e)
{
GridItem giSelected = e.NewSelection;
if ((giSelected != null) && (giSelected.PropertyDescriptor != null))
{
string sDescription = GetCurrentPropertyDescription(giSelected.PropertyDescriptor.Name);
if ((sDescription != null) && (sDescription != giSelected.PropertyDescriptor.Description))
{
MethodInfo miSetStatusBox = pgConfig.GetType().GetMethod("SetStatusBox", BindingFlags.NonPublic | BindingFlags.Instance);
if (miSetStatusBox != null)
miSetStatusBox.Invoke(pgConfig, new object[] { giSelected.PropertyDescriptor.DisplayName, sDescription });
}
}
}
In the code sample, GetCurrentPropertyDescription is a private function that retrieves the current property description of the object being shown in the property grid (m_da.Config in my case):
private string GetCurrentPropertyDescription(string sPropertyName)
{
PropertyDescriptor oPropDescriptor = TypeDescriptor.GetProperties(m_da.Config.GetType())[sPropertyName];
if (oPropDescriptor != null)
{
DescriptionAttribute oDescriptionAttr = (DescriptionAttribute)oPropDescriptor.Attributes[typeof(DescriptionAttribute)];
if (oDescriptionAttr != null)
return oDescriptionAttr.Description;
}
return null;
}
My solution is less suitable than huoxudong125's if you want full globalization, but if you just want dynamic descriptions for some of your properties without changing the inheritance of the object being shown, it is an option.
Disadvantage of my method is that the underlying cached PropertyDescriptor objects of the grid are never updated, so SetStatusBox will always be called twice if a property with changed description is selected, which is inefficient.
The solution of huoxudong125 is one possible solution. I'd like to offer another one (but without talking about how to change the culture stuff at runtime - you can google that yourself ;) ). For myself I started with using localized subclasses for DisplayName, Description and Category.
As we know, DisplayName does update to current culter when PropertyGrid is updated, but Description and Category do not. I think the reason for this is on reflection level, when PropertyGrid requests category and description. As you can see, those values are cached on first read but displayName is not. To tackle that I developed two solution (where the first one is weird and I do not understand why that works myself..). Both revolve around an extra TypeDescriptionProvider
The base
First goes the custom TypeDescriptionProvider, which can be bound to any class via attribute:
internal class UpdateableGlobalizationDescriptionProvider<TTargetType> : TypeDescriptionProvider
{
private static TypeDescriptionProvider defaultTypeProvider = TypeDescriptor.GetProvider(typeof(TTargetType));
public UpdateableGlobalizationDescriptionProvider() : base(defaultTypeProvider) { }
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
var result = base.GetTypeDescriptor(objectType, instance);
return new ForcedGlobalizationTypeDescriptor(result);
}
}
First solution
This one revolves around "just get it done"... Add a CustomTypeDescriptor implementation, which wraps original PropertyDescriptor instances with the custom ones:
internal class ForcedGlobalizationTypeDescriptor : CustomTypeDescriptor
{
readonly ICustomTypeDescriptor inner;
public ForcedGlobalizationTypeDescriptor(ICustomTypeDescriptor typeDescriptor) : base(typeDescriptor)
{
inner = typeDescriptor;
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
// First solution
var result = base.GetProperties(attributes);
var transformed = result.OfType<PropertyDescriptor>().Select(d => new ForcedPropertyDescriptor(d)).ToArray();
return new PropertyDescriptorCollection(transformed);
}
}
and outer the PropertyDesciptor simply returns values from the wrapped PropertyDescriptor. The simplest implementation for PropertyDescriptor I found - tell me, if there is a shorter one, please.
internal class ForcedPropertyDescriptor : PropertyDescriptor
{
private PropertyDescriptor innerDescriptor;
public ForcedPropertyDescriptor(PropertyDescriptor descriptor) : base(descriptor)
{
innerDescriptor = descriptor;
}
// important:
public override string Category => base.Category;
public override string Description => base.Description;
public override Type ComponentType => innerDescriptor.ComponentType;
public override bool IsReadOnly => innerDescriptor.IsReadOnly;
public override Type PropertyType => innerDescriptor.PropertyType;
public override bool CanResetValue(object component) => innerDescriptor.CanResetValue(component);
public override object GetValue(object component) => innerDescriptor.GetValue(component);
public override void ResetValue(object component) => innerDescriptor.ResetValue(component);
public override void SetValue(object component, object value) => innerDescriptor.SetValue(component, value);
public override bool ShouldSerializeValue(object component) => innerDescriptor.ShouldSerializeValue(component);
}
I think it works, because for every read of category or description there is a new ForcedPropertyDescriptor, which has not cached the value, yet. At the same time this is a drawback: for like every request on the Category or Description propery a new instance of ForcedPropertyDescriptor is created, while Microsoft's implementation seems to cache craeated PropertyDescriptors somewehere.
Second solution
To avoid that instance creation every time, I simply stored every seen ProperyDescriptor created by ForcedGlobalizationTypeDescriptor in a set. And as soon as a localization change appears, that set gets called to reset it's items cached values:
internal class DescriptorReset
{
public static DescriptorReset Default { get; } = new DescriptorReset();
private HashSet<MemberDescriptor> descriptors = new HashSet<MemberDescriptor>();
public void Add(MemberDescriptor descriptor)
{
descriptors.Add(descriptor);
}
private void RunUpdate()
{
if (descriptors.Count == 0)
return;
FieldInfo category, description;
category = typeof(MemberDescriptor).GetField(nameof(category), BindingFlags.NonPublic | BindingFlags.Instance);
description = typeof(MemberDescriptor).GetField(nameof(description), BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var descriptor in descriptors)
{
category.SetValue(descriptor, null);
description.SetValue(descriptor, null);
}
}
}
The RunUpdate method uses Reflection to reset inner fields to null, so on the next call to corresponding properties the localized values are read again.
All you need now is some magic to call RunUpdate at the right moment. For myself I have a class in my core solution, which provides a method to set a new CultureInfo. When called, it sets the default ui culture and the default culture to the new CultureInfo and raises two events: the first one is to update all internal logic and the second one is for everything based on internal logic, like the GUI.
And since I do not know where and how long Microsoft's PropertyDescriptors are stored, I created a HashSet with WeakReference (based on WeakHashTable) to store corresponding references.
Usage
Simply append the DescriptionProvider class to your class shown in PropertyGrid:
[LocalizedDescription(nameof(MyClass), typeof(MyTextResource))]
[TypeDescriptionProvider(typeof(ForcedGlobalizationTypeDescriptor<MyClass>))]
class MyClass
{
// ...
The way your LocalizedDescription works, depends on you...
here a solution is given to get value of a property of a class by supplying its name . now I wonder how I can do the same in this condition :
I have a class MyClass . this class ha a property of type Foo named foo . the Foo has a property of type Bar named bar . and bar has a string property named value .
properties are not static .
I want to be able to get value of foo.bar.value by passing the string "foo.bar.value" as propertyName . in other word I want to pass the property path to get its value .
is it possible ?
You can do this with a recursive method. Each call takes the value with the first word in path and call the method again with the rest of the part.
public object GetPropertyValue(object o, string path)
{
var propertyNames = path.Split('.');
var value = o.GetType().GetProperty(propertyNames[0]).GetValue(o, null);
if (propertyNames.Length == 1 || value == null)
return value;
else
{
return GetPropertyValue(value, path.Replace(propertyNames[0] + ".", ""));
}
}
This assumes that the properties are named like the classes. i.e. that the property of Type Foo is also named Foo. Without this assumption, the question is lacking some crucial information.
You can use the string.Split method to separate the string foo.bar.value at the dots. You will then have an array with one element per property name.
Iterate over that array and use PropertyInfo.GetValue to retrieve the value of the properties. The value returned in one operation is the instance passed to GetValue in the following iteration.
string props = "foo.bar.value";
object currentObject = // your MyClass instance
string[] propertyChain = props.Split('.');
foreach (string propertyName in propertyChain) {
if (propertyName == "") {
break;
}
PropertyInfo prop = currentObject.GetType().GetProperty(propertyName);
currentObject = prop.GetValue(currentObject);
if (currentObject == null) {
// somehow handle the situation that one of the properties is null
}
}
Update: I have added a safeguard to ensure this will work even if props is empty. In that case, currentObject will remain a reference to the original MyClass instance.
Assuming FOO is static, you can get the class from a string like this:
C# Reflection: How to get class reference from string?
...and then use the rest of the post you've linked to to get the property and value from there:
Get property Value by its stringy name
If FOO isn't static, you'll need to use reflection on the instance (which would negate the requirement to pass in the name of the class as a string, since you can get the class from the instance with GetType()) - remembering that Bar won't have a value in the class unless it is static.
As You pointing to answer of the question here , You need to make use of Reglection to achieve same thing.
With help of reflection you can read value of property.
something like this,
// dynamically load assembly from file Test.dll
Assembly testAssembly = Assembly.LoadFile(#"c:\Test.dll");
// get type of class Calculator from just loaded assembly
Type calcType = testAssembly.GetType("Test.Calculator");
// create instance of class Calculator
object calcInstance = Activator.CreateInstance(calcType);
// get info about property: public double Number
PropertyInfo numberPropertyInfo = calcType.GetProperty("Number");
// get value of property: public double Number
double value = (double)numberPropertyInfo.GetValue(calcInstance, null);
you need a put the code in one function and than split string as per you requirement
public object getvalue(string propname)
{
//above code with return type object
}
String[] array = string.Split("foo.bar.value");
//call above method to get value of property..
Read for detail : http://www.csharp-examples.net/reflection-examples/
In a base class I have the following method for derived classes:
protected virtual void SetValue<T>(ref T field, string propertyName, T value)
{
//assign value to the field and do some other staff
...
}
Is there any way to check if fieldVar has an attribute applied (for example DataMemberAttribute)?
No, there is no way to do that, except that it looks like you're also told the property name.
If you can find the FieldInfo of the field, then you can find any attributes, but not through the ref-parameter alone.
Reading between the lines you have a set of private fields which back the values of public properties. On some or all of these properties you some data attributes attached that you want to discover.
PropertyInfo pi = this.GetType().GetProperty(propertyName);
object[] dataMemberAttributes = pi.GetCustomAttributes(typeof(DataMemberAttribute, true);
if (dataMemberAttributes.Length > 0)
{
DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)dataMemberAttributes[0];
// Do stuff with the attribute.
}
1) I'm still quite new to programming and have read a bit about getters and setters. But I really don't understand why they are used.. Could anyone explain it, or point me to an article? (The ones I read were not really understandable for me...)
2) In my current project I have a class where I declare and initialize an array of structs. I now need to access the array from another class, but it gives me the error: An object reference is required to access non-static member 'BaseCharacter.Attributes'.
I figures this could mean I need to use getters and setters? But how does this work for arrays?
Thanks in advance!
Simon.
EDIT: 2nd question got solved, which brings me to something else. When I want to use some class in another one, I'm making a new instance of the class, right? And this means I get the original values?
But that's not what I want.
The second class is used to generate the UI, and needs the values I'm keeping in the first class.
At some point I will implement save files (XML or even on a server in later stage). Can I then just get the values from those files?
For the getters and setters (the things that use them are called Properties) it's just a convenient and nice-looking way to make people think they're using a variable, but to do some computation whenever the variable is updated or accessed. For instance:
BankAccount.Interest
looks better than
BankAccount.GetInterest()
Even though you can calculate the interest at the time it is requested in both cases.
They are also used to make a variable be able to be accessed from outside the class, but changeable only from within the class with this technique:
public double Interest {
get;
private set;
}
For an example of a setter being used, if you've ever used Windows Forms and updated a control's Height or Width property, you're using a setter. While it looks like you're using a normal instance variable like c.Height = 400, you're really letting c update it's position by redrawing at a new place. So setters notify you exactly when a variable is changed, so your class can update other things base on the new value.
Yet another application of Properties is that you can check the value people try to set the property to. For instance, if you want to maintain an interest rate for each bank account but you don't want to allow negative numbers or numbers over 50, you just use a setter:
private int _interestRate = someDefault;
public int InterestRate {
get { return _interestRate; }
set {
if (value < 0 || value > 50)
throw new SomeException(); // or just don't update _interestRate
_interestRate = value;
}
}
This way people can't set public values to invalid values.
For your second question, you can do one of two things depending on what you're trying to do.
One: You can make that member static. That means that just one of them exists for the entire class instead of one per instance of the class. Then you can access it by ClassName.MemberName.
You can do that this way:
// inside the BaseCharacter class definition:
public static SomeStruct[] Attributes = new SomeStruct[size];
// then to use it somewhere else in your code, do something with
BaseCharacter.Attributes[index]
Two: You have to make an instance of the class and access the array through that. This means that each object will have its own seperate array.
You'd do that like this:
BaseCharacter bc = new BaseCharacter();
// use bc.Attributes
The second one is probably what you'll want to do, since you probably will want to modify each character's attributes seperately from all the other characters.
Actually the error you mention is not related to the getters and setters concept, it's because after creating your class you need to create an object before using its members; think of the class as a template for a document and the object as the document
you are most likely doing something like this:
var someVar = BaseCharacter.Attributes;
When you should be doing something like this:
var someVar = new BaseCharacter();
var someOtherVar = someVar.Attributes;
And about why the getters and setters, Seth Carnegie's Answer covers it nicely.
If you are new to Object Oriented Programming, you may be missing an important concept, that is about encapsulation.
Fields (attributes) of a class should be accessed only from within the class (or it's inherited classes). That is, if we have a class person, only with a name, you can do
public class Person
{
public string Name;
}
So anywhere in your program, you will be able to access it by doing:
Person person = new Person();
person.Name = "Andre";
This works, but it's not encapsulated. In some languages like C++ or Java, it was done like this:
public class Person
{
private string _name;
public string setName(string newName)
{
this._name = newName;
}
public string getName()
{
return this._name;
}
}
Person person = new Person();
person.setName("Andre");
This makes our _name attribute encapsulated, it can only be retrieved by it's get and set methods (that is, by the interface of the class).
C# makes this easier, allowing getters and setters:
public class Person
{
private string name;
public string Name
{
get { return this.name; }
set { this.name = value; }
}
}
Person person = new Person();
person.Name = "Andre";
This is very much like the second example (Java/C++ way), but you treat Name as property, instead of methods, and leaving our name property encapsulated
1) They might seem optional but they allow you more control over code:
You're able to intercept new values and avoid them being set (e.g. to exclude pointless values). Also you're able to fire custom events in case a property is changed or updated (just like the WinForms controls do).
private string name;
public string Name
{
get
{
// running additional code, e.g. here I avoid returning 'null' for a name not set
if(name == null)
return "(Unknown)";
return name;
}
set
{
// checking incoming values, e.g. here I avoid setting an empty name
name = value != null && value.Length > 0 ? name : null;
// running more/additional code, e.g. here I raise an event
if(OnNameChange)
OnNameChange();
}
}
2) Without knowing the code it's hard to tell you the exact reason, but if you'd like to access some member variable or property you have to either create an object of that class or make the variable static (e.g. shared between all instances of the object):
class MyClass
{
public static int someNumber = 55;
public int thisNumber;
}
// ...
int someothervar = MyClass.someNumber; // access the static member variable
MyClass mc = new MyClass(); // create an object of the class
int yetanothervar = mc.thisNumber; // access the member variable