I have code as follow:
struct Name
{
private int age;
public int Age
{
get
{
return age;
}
set
{
age = Age;
}
}
public Name(int a)
{
age = a;
}
};
class Program
{
static void Main(string[] args)
{
Name myName = new Name(27);
Console.WriteLine(myName.Age);
myName.Age = 30;
Console.WriteLine(myName.Age);
}
}
As you can see I am trying to change value of Age using my property. But i am getting still the same value which I pass when I am creating a object of struct Name. I know that struct are immutable in C#, but i thought i would bee able to change field in them. I am very confused. Could someone explain me what is going on?
Structs in C# are not immutable by default. To mark a struct immutable, you should use the readonly modifier on the struct. The reason your properties aren't updating is that your syntax is wrong. You should use the value keyword to dereference the value provided to the setter.
public int Age
{
get
{
return age;
}
set
{
age = value; // <-- here
}
}
Related
I am trying to create a method that increases the family age by one. Passing arguments to method is extremely difficult for me, especially with this problem. I believe there is a better way of doing this, but this is the only way that can come to mind. I am currently having trouble trying to display all 4 of the families age into one label, after their age is increased by one. This is currently what I have:
// Create a method using the increment operator that increates the age of 4 family members by 1
private void TotalAge (int age1/*, int age2, int age3, int age4*/)
{
age1++;
/* age2++;
age3++;
age4++; */
}
private void calculateButton_Click(object sender, EventArgs e)
{
// Variables to hold family member ages
int member1 = 0, member2 = 0, member3 = 0, member4 = 0;
// Conver the ages into a string, using tryparse method
if (int.TryParse(ageTextBox1.Text, out member1))
{
if (int.TryParse(ageTextBox2.Text, out member2))
{
if (int.TryParse(ageTextBox3.Text, out member3))
{
if (int.TryParse(ageTextBox4.Text, out member4))
{
displayAge.Text = Convert.ToString(TotalAge(member1))
}
}
}
}
}
you should create a FamilyMember class that will contain all the properties of a family member (age, name, etc.) then you simply pass a list or an array of family members to a function that will increment in 1 the age of every FamilyMember in the array.
Public class FamilyMember
{
public string Name {private set; get;}
public int Age { private set; get; }
public FamilyMember(string name, int age)
{
Name = name;
Age = age;
}
public void IncrementAge()
{
Age++;
}
}
Public void IncrementAgeOfFamilyMembers(List<FamilyMember> FamilyMembers)
{
foreach (var fmember in FamilyMembers)
fmember.IncrementAge();
}
if you want it to be for exactly 4 family members just change the List<FamilyMember> to FamilyMember[4] (but then you should check for nulls)
I have this code:
using System;
using System.Collections.Generic;
using UnityEngine;
public interface HaveId
{
int id { get; }
}
public struct BusinessData : HaveId
{
// business type data
public int graphic_asset_id;
public string name;
public int id { get; set; }
}
public class LookupHelper<T> where T: HaveId
{
private T[] _list;
public T[] list
{
get { return _list; }
set { _list = value; _mapToDictionary(); }
}
private Dictionary<int, int> idxById = new Dictionary<int, int>();
public LookupHelper(){}
private void _mapToDictionary()
{
if(idxById.Count > 0) idxById = new Dictionary<int, int>();
for(var z =0 ; z < list.Length; ++z)
{
idxById[list[z].id] = z;
}
}
public bool IsIdExists(int id)
{
return idxById.ContainsKey(id);
}
public T ById(int id) // is this a reference?
{
var idx = idxById[id];
if (idx >= list.Length) throw new Exception(
String.Format("Invalid Index: {0} >= {1} on {2}",idx.ToString(),list.Length.ToString(), typeof(T).Name)
);
return list[idx];
}
}
And the test code:
LookupHelper<BusinessData> bd = new LookupHelper<BusinessData>();
bd.list = new BusinessData[]
{
new BusinessData{id = 1, name = "test"},
new BusinessData{id = 2, name = "test2"},
};
bd.ById(1).name = "foo";
This give an error: "Cannot modify struct member when accessed struct is not classified as a variable"
How can I change the value of first BusinessData and keep the array still allocated on a contiguous memory (array of struct, needed for cache locality)?
This should be a simple matter of splitting it up into a few lines. Extract the object to get a copy, modify the copy, then overwrite it in the array:
BusinessData bsd = bd.ById(1);
bsd.name = "foo";
bd.SetById(1, bsd);
Of course, you'll need to write that SetById method to reinsert things into the array:
public void SetById(int id, T obj)
{
Int32 idx = idxById[id];
list[idx] = obj;
}
As you know C# borrowed something’s from C and Java. But not everything.
In C, you can create a place for struct on the stack or the heap. On the heap, I can then pass a pointer around and change the content. Very powerful.
But C# emphasizes ease of memory management via garbage collection. To make it easy, C# has the concept of boxing value types into System.Object. Additional details, can be found on Microsoft C# Programming Guide on Boxing and unboxing.
So when you access the value type in your list, you have to explicitly unbox the value. Therefore it’s a copy of the item in the list. You can do what #Nyerguds suggested.
But to make life easy, why not turn your BusinessData into a class?
I may not have a good grasp of the ?? operator yet and ran into a design flaw I couldn't explain.
Compare the following two properties, the only difference being how there are initialized: the first explicitly initialized, while the second with the ?? operator (or am I doing it wrong here?).
If I run data init with both properties, the collection based on the first property comes up populated as expected, while the second one with the ?? operator never gets populated and comes up with 0 elements in the collection.
Surely something is wrong here in my assumption; what is the flaw here?
P.S. Please ignore the Set method which is to implement INotifyPropertyChanged in the base class and has no bearing on this issue (which is confined to the type of initialization).
// property version 1
private ObservableCollection<UserName> _userNameColl = new ObservableCollection<UserName>();
public ObservableCollection<UserName> UserNameColl
{
get { return _userNameColl; }
set { Set(ref _userNameColl, value); }
}
// property version 2
private ObservableCollection<UserName> _userNameColl;
public ObservableCollection<UserName> UserNameColl
{
get { return _userNameColl ?? new ObservableCollection<UserName>(); }
set { Set(ref _userNameColl, value); }
}
// a simple class for creating object collection
public class UserName
{
public string Name { get; set; }
public int Age { get; set; }
public string Email { get; set; }
}
// a simple test populating the collection
for (int i = 0; i < 4; i++)
{
// silly data init just for test
UserNameColl.Add(new UserName()
{
Name = $"UserName No {i}",
Age = 20 + i,
Email = $"email{i}#local.lan"
});
}
The second one never initializes your field but always returns a new collection. Try this one instead:
public ObservableCollection<UserName> UserNameColl
{
get { return _userNameColl ?? (_userNameColl = new ObservableCollection<UserName>()); }
set { Set(ref _userNameColl, value); }
}
Is it somehow possible for properties to reference each other during the creation of a dynamic object an anonymously-typed object (i.e. inside the object initializer)? My simplified example below needs to reuse the Age property without making a second heavy call to GetAgeFromSomewhere(). Of course it doesn't work. Any suggestion on how to accomplish this?
var profile = new {
Age = GetAgeFromSomewhere(id),
IsLegal = (Age>18)
};
Is something like this possible or not possible with dynamic objects anonymously-typed object initializers?
Unfortunately it's not possible, even with explicitly typed objects. This is because of the way object initializers work. For example:
public class MyClass
{
public int Age = 10;
public bool IsLegal = Age > 18;
}
Yields this compiler error at "IsLegal":
Error 1 A field initializer cannot reference the non-static field,
method, or property 'MyClass.Age' ...
Field initializer can't reference other non-static fields, and since anonymous types don't create static fields, you can't use the value of one field to initialize another. The only way around this, is to declare the variables outside the anonymous type and use them inside the initializer.
int age = GetAgeFromSomewhere(id);
var profile = new {
Age = age,
IsLegal = age > 18
};
Don't complicate thing, keep it simple
//Create a variable
var age = GetAgeFromSomewhere(id);
var profile = new {
Age = age,
IsLegal = age>18
}
What you want is not possible within object intializers. You cannot read properties of the object being initialized. (It does not matter, whether the type is anonymous or not.)
Instead, Create a class
public class Profile
{
public Profile(int id)
{
Age = GetAgeFromSomewhere(id);
}
public int Age { get; private set; }
public int IsLegal { get { return Age > 18; } }
}
Or getting the age the lazy way:
public class Profile
{
private readonly int _id;
public Profile(int id)
{
_id = id;
}
private int? _age;
public int Age {
get {
if (_age == null) {
_age = GetAgeFromSomewhere(_id);
}
return _age.Value;
}
}
public int IsLegal { get { return Age > 18; } }
}
or using the Lazy<T> class (starting with Framework 4.0):
public class Profile
{
public Profile(int id)
{
// C# captures the `id` in a closure.
_lazyAge = new Lazy<int>(
() => GetAgeFromSomewhere(id)
);
}
private Lazy<int> _lazyAge;
public int Age { get { return _lazyAge.Value; } }
public int IsLegal { get { return Age > 18; } }
}
Call it like this
var profile = new Profile(id);
If you don't want to have unnecessary variable, I suggest you use the current object instead :
var profile = new
{
Age = GetAgeFromSomewhere(id),
};
profile.IsLegal = profile.Age > 18;
I am looking for help in determining if the class model that I am building can be improved upon. The class that I am building is a simple Product class with a few attributes.
class clsProducts
{
private string _name;
private double _productionRate;
//Constructor
public clsProducts()
{
_name = "null";
_productionRate = 0.0;
}
public clsProducts(string name, double productionRate)
{
_name = name;
_productionRate = productionRate;
}
//Properties
public string Name
{
get { return _name; }
}
public double ProductionRate
{
get { return _productionRate; }
}
}
What I would like to add is the ability to have the monthly forecasted values for each product in the class. I could add the following to do this
private double _janValue;
private double _febValue;
and so on, but this seems messy. I also considered creating a nested class called ForecastValues, such as
class clsProducts
{
...code here....
protected class ForecastValues
{
private string name;
private double forecastValue;
...other code.....
}
}
however, I am not sure that this idea would even work. Can any one suggest a way for me to handle this cleanly?
Thank you
A few things here.
I would recommend removing the cls hungarian prefix from the class name.
Depending on exactly what your "ForecastValues" are. You could make a property on the "Product" class that is a List, or possibly a Dictionary. My guess is that you might be able to go the dictionary route with ease.
I would suggest just to use an array and an indexer.
public enum Month
{
January = 1, February = 2, March = 3,
April = 4, May = 5, June = 6,
July = 7, August = 8, September = 9,
October = 10, November = 11, December = 12
}
public class Product
{
private readonly String name = null;
private readonly Double productionRate = 0.0;
private readonly Double[] productionRateForcast = new Double[12];
public Product(String name, Double productionRate)
{
this.name = name;
this.productionRate = productionRate;
}
public String Name { get { return this.name; } }
public Double ProductionRate { get { return this.productionRate; } }
public Double this[Month month]
{
get { return this.productionRateForcast[month - Month.January]; }
set { this.productionRateForcast[month - Month.January] = value; }
}
}
I am not sure if month - Month.January requires an explicit cast to Int32. Alternativly one could start with January = 0 but this seems a bit odd, too.
I did also some code changes. I removed the default constructor, because I see no value in a Product instance with "uninitialized" fields and no possibilty to alter them later. In consequence I made the fields readonly, too. Finaly I removed the Hungarion notation prefix - this is a quite an outdate coding style - and turned Products into Product because it represents one product not a collection of products.
UPDATE
To catch up the dictionary idea .... I will just give the required changes.
private readonly IDictionary<Month, Double> productionRateForcast =
new Dictionary<Month, Double>();
public Double this[Month month]
{
get { return this.productionRateForcast[month]; }
set { this.productionRateForcast[month] = value; }
}
This might be a even cleaner solution then using an array. You could also just expose the dictionary through a property instead of having an indexer, but I consider the indexer a cleaner solution because it hides some implementation details.
public IDictionary<Month, Double> ProductionRateForcast
{
return this.productionForecast;
}
In all case the usage would be as follows.
Product myProduct = new Product("Great Product", 0.8);
myProduct[Month.August] = 0.7;
This looks quite odd. One could try adding a IndexerNameAttribute to the indexer, but I am not sure if this would allow to write
myProduct.ProductionValueForcast[Month.August] = 0.7;
in a language with indexer support. So I finally tend to change my mind and prefer exposing the dictionary by a property if the IndexerNameAttribute does not help.
I don't think nested classes are a great idea. What I would do is create an additional class 'ForecastValues' but mark it as 'internal protected'. That way you can use it within your assembly but users of your code will only be able to reference it when it contains values.
-Shaun
This is what I would do,
class ClsProducts
{
//Constructor
public ClsProducts()
{
Name = "null";
ProductionRate = 0.0;
}
public ClsProducts(string name, double productionRate)
{
Name = name;
ProductionRate = productionRate;
}
//Automatic properties with private setters
public string Name { get; private set; }
public double ProductionRate { get; private set; }
//since you basically have key value pair, why not use one?
public KeyValuePair<String,Double> Forcast{ get; set; }
}