Assignment to struct array inside method does not work in C#? - c#

Here is the code snippet from my LinqPad:
public class Elephant{
public int Size;
public Elephant()
{
Size = 1;
}
}
public struct Ant{
public int Size;
}
private T[] Transform2AnotherType<T>(Elephant[] elephantList)
where T:new()
{
dynamic tArray = new T[elephantList.Length];
for (int i = 0; i < elephantList.Length; i++)
{
tArray[i] = new T();
tArray[i].Size = 100;
//tArray[i].Dump();
}
return tArray;
}
void Main()
{
var elephantList = new Elephant[2];
var elephant1 = new Elephant();
var elephant2 = new Elephant();
elephantList[0] = elephant1;
elephantList[1] = elephant2;
elephantList.Dump();
var r = Transform2AnotherType<Ant>(elephantList);
r.Dump();
}
I want to change one object array of known type,Elephant,to another object array of type T. T is not a class,but limited to struct which
provided by the already existed API.And every instance of type T shares some common property,says Size,but also has their own particular property which
I have omitted in my example code.So I put dynamic keyword inside the Transform2AnotherType<T>.
And I could not even to use Dump to make sure if the assignment has made effect,thus will throw RuntimeBinderException.
My question is: how to correctly make the assignment in such a struct array and return it back properly?

I suggest change your code like this:
public class Elephant
{
public Elephant()
{
Size = 1;
}
public int Size { get; set; }
}
public struct Ant
{
public int Size { get; set; }
}
private static T[] Transform2AnotherType<T>(Elephant[] elephantList)
where T : new()
{
T[] tArray = new T[elephantList.Length];
for (int i = 0; i < elephantList.Length; i++)
{
dynamic arrayElement = new T();
arrayElement.Size = 100;
tArray[i] = arrayElement;
//tArray[i].Dump();
}
return tArray;
}
static void Main()
{
var elephantList = new Elephant[2];
var elephant1 = new Elephant();
var elephant2 = new Elephant();
elephantList[0] = elephant1;
elephantList[1] = elephant2;
//elephantList.Dump();
var r = Transform2AnotherType<Ant>(elephantList);
//r.Dump();
}

Related

(C# VB) Could I change all variables' values in a class dynamically?

I've seen ways to list all variables in a certain class, but is there a way to set their values?
I want a method I can call, and it can reset all variables' values to false / 0 , I know I can manually set those to false / 0, but any change to their values would mess up everything, and I'm just seeing if there's anything more dynamic / easy way that this could be done ?
Currently I'm doing the following:
// These are just some of the vars that are here.
Error = false;
double GUM = 0, MUM = 0;
decimal Mass = 0, GAcc = 0, Fg = 0, FµsRes = 0, FµkRes = 0, Fµs = FµsNS.Value, Fµk = FµkNS.Value;
As others have mentioned, you could use Reflection to set all the class fields to their default values:
using System;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var myClass = new MyClass();
myClass.Error = true;
myClass.GUM = 1;
myClass.MUM = 2;
myClass.Mass = 3.5m;
myClass.GAcc = 4.5m;
myClass.Fg = 5.5m;
ResetFields(myClass);
// All fields are reseted
}
public static void ResetFields(object source)
{
foreach (var fieldInfo in source.GetType().GetFields() )
{
fieldInfo.SetValue(source, GetDefault(fieldInfo.FieldType) );
}
}
public static object GetDefault(Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
}
public class MyClass
{
public bool Error = false;
public double GUM = 0, MUM = 0;
public decimal Mass = 0, GAcc = 0, Fg = 0;
//etc etc
}
}
A better approach however, is to create a class to only hold the values and re-create it when needed:
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var myClass = new MyClass();
myClass.Variables = new MyVariables();
myClass.Variables.Error = true;
myClass.Variables.GUM = 1;
myClass.Variables.MUM = 2;
myClass.Variables.Mass = 3.5m;
myClass.Variables.GAcc = 4.5m;
myClass.Variables.Fg = 5.5m;
myClass.Variables = new MyVariables();
// All fields are reseted
}
}
public class MyVariables
{
public bool Error = false;
public double GUM = 0, MUM = 0;
public decimal Mass = 0, GAcc = 0, Fg = 0;
}
public class MyClass
{
public MyVariables Variables;
// etc etc
public int Xyz
{
get { return Variables.GUM + Variables.MUM; } // Use calculated properties
}
}
}
The GetDefault method is shown in this answer.

Having trouble creating random values

So I created Pairs class that contains int and double and I want to create an array of them with my
array class by creating random values, but I'm getting System.NullReferenceException at Line 19 of
my array class.
Here's my pair class
class Pair
{
public int integer = 0;
public double doubl = 0.0;
public Pair(int integer, double doubl)
{
this.integer = integer;
this.doubl = doubl;
}
public Pair()
{
}
public int Integer() { return integer; }
public double Doubl() { return doubl; }
}
And this is my array class and the abstract class
class MyDataArray : DataArray
{
Pair[] data;
int operations = 0;
public MyDataArray(int n, int seed)
{
data = new Pair[n];
Random rand = new Random(seed);
for (int i = 0; i < n; i++)
{
data[i].integer = rand.Next(); //I get error here
data[i].doubl = rand.NextDouble();
}
}
public int integer(int index)
{
return data[index].integer;
}
public double doubl(int index)
{
return data[index].doubl;
}
}
abstract class DataArray
{
public int operations { get; set; }
public abstract void Swap(int i, int j);
public abstract void Swap2(int i, int high);
}
Also is it even worth it using this abstract class I used this from a reference that my university
provided. I have to create an quicksort algorithm that sorts pairs in arrays and linked lists and analyze it.
data = new Pair[n];
This creates a new array of null references.
The loop should be
for (int i = 0; i < n; i++)
{
data[i] = new Pair(rand.Next(), rand.NextDouble())
}
While we are looking at your code: you are making a good attempt here to make an immutable pair, but it could be better. What you want is:
class Pair
{
public Pair(int integer, double doubl)
{
this.Integer = integer;
this.Double = doubl;
}
public int Integer { get; private set; }
public double Double { get; private set; }
}
Shorter, safer, clearer.
The issue with your code is that you are only initializing the array of data in MyDataArray. When creating an array of instances, it only initializes references for the array, not the actual instances to be in the array. Those references all point to null. So when you do try to set the integer member of the i'th Pair instance in the data array:
...
data[i].integer = rand.Next();
...
You are actually trying to set the integer member of null, which does not exist.
...
null.integer = rand.Next();
...
To fix this, simply create a new instance of Pair for each index of data in your loop.
...
for (int i = 0; i < n; i++)
{
data[i] = new Pair();
data[i].integer = rand.Next();
data[i].doubl = rand.NextDouble();
}
...
Even better, you can use the constructor you've made where it takes parameters to set integer and doubl upon construction to simplify the code in your loop.
...
for (int i = 0; i < n; i++)
{
data[i] = new Pair(rand.Next(), rand.NextDouble());
}
...

Unable to add new object to List past index 0

I'm noticing that I am unable to add to a list of objects past index 0. Any other index returns a null reference.
public class MultiValidation
{
public List<SingleValidation> validations { get; set; }
public MultiValidation(List<string> numArray)
{
for(int i = 0; i<numArray.Count; i++)
{
SingleValidation individual = new SingleValidation(Validate.idArray[i], Validate.actionArray[i], Validate.expiryArray[i]);
validations = new List<SingleValidation>();
validations.Add(individual);
Console.WriteLine(validations[i].action);
}
}
Here is the constructor used for SingleValidation
public SingleValidation(string ide, string ac, string exDate)
{
this.action = ac;
this.expiry = exDate;
this.id = ide;
}
I have tested that idArray[i],actionArray[i],expiryArray[i] are all strings.
validations = new List<SingleValidation>();
This should be outside the for loop.
{
validations = new List<SingleValidation>();
for(int i = 0; i<numArray.Count; i++)
{
SingleValidation individual = new SingleValidation(Validate.idArray[i],
Validate.actionArray[i], Validate.expiryArray[i]);
validations.Add(individual);
Console.WriteLine(validations[i].action);
}
}

Using enum item to call a method

I have an enum with 30 items in it. Each item has a corresponding function with the same name. I would like to be able to call the function by referencing the enum at a certain position.
So if the value at enum[0] = Foo, I would like to be able to call Foo(string bar) by using something like enum(0)("foobar")
In the end the point is I am running each function as a task like so:
enum Test { AA, BB, CC, DD ....}
tasks[0] = Task.Run(() => { prices[0] = AA("a string"); });
tasks[1] = Task.Run(() => { prices[1] = BB("a string"); });
tasks[2] = Task.Run(() => { prices[2] = CC("a string"); });
//for 30 tasks
What I would like to do is something along the lines of:
enum Test { AA, BB, CC, DD ....}
for (int i = 0; i < 30; i++)
{
tasks[i] = Task.Run(() => { prices[i] = (Test)i("a string"); });
}
Task.WaitAll(tasks.ToArray());
Is this something that is even possible?
EDIT:
The enum relates to controls on a form so i have an array of textboxs, label and a array of prices that is populated with the results of the functions:
enum Dealers { Dealer1, Dealer2 ... Dealer29, Dealer30 };
static int noOfDealers = Enum.GetNames(typeof(Dealers)).Length;
decimal[] prices = new decimal[noOfDealers];
TextBox[] textBox = new TextBox[noOfDealers];
Label[] boxes = new Label[noOfDealers];
for (int i = 0; i < noOfDealers; i++)
{
textBox[i] = Controls.Find("txt" + (Dealers)i, true)[0] as TextBox;
boxes[i] = Controls.Find("box" + (Dealers)i, true)[0] as Label;
prices[i] = 0;
}
//RUN 30 TASKS TO POPULATE THE PRICES ARRAY
for (int i = 0; i < noOfDealers; i++)
{
textBox[i].Text = "£" + prices[i].ToString();
}
//LOOP THROUGH PRICES ARRAY AND FIND CHEAPEST PRICE, THEN COLOUR THE LABEL BACKGROUND GREEN FOR THE TEXT BOX WITH THE NAME AT ENUM VALUE WHATEVER I IS
I guess i am just trying to make my code as concise as possible, there is the potential for the amount of tasks to double and didn't want to end up with 60 lines to populate the tasks array
I would create dictionary and map enum to actions:
Dictionary<Test, Func<string,double>> actions = new Dictionary<Test, Func<string,double>>()
{
{Test.AA, (x) => { return 5;}},
{Test.BB, (x) => { return 15; }},
}; //x is your string
var res = actions[Test.AA]("hello");
I would strongly suggest using a built in construct - like an extension method and a simple switch:
public static int GetPriceWithString(this Test test, string str)
{
switch (test)
{
case Test.AA:
break;
case Test.BB:
break;
case Test.CC:
break;
case Test.DD:
break;
default:
throw new ArgumentOutOfRangeException(nameof(test), test, null);
}
}
then your loop looks almost the same:
for (int i = 0; i < 30; i++)
{
tasks[i] = Task.Run(() =>
{
prices[i] = ((Test)i).GetPriceWithString("a string");
});
}
What you want to do is possible with reflection, which can be a powerful tool - but ideally should only be used as a last resort, as it will hide what could be compile time errors, and cause less code readability.
Using a simple switch like this makes your code self-documented, so when you come back to this in a month you can quickly remember what the intention was.
How about using an array of delegates:
using System;
using System.Threading.Tasks;
namespace ConsoleApplication
{
class Program
{
private static int AA(string a) { return 0; }
private static int BB(string a) { return 1; }
private static int CC(string a) { return 2; }
private static Func<string, int>[] functions = new Func<string, int>[] { AA, BB, CC };
private static int[] prices = new int[functions.Length];
private static Task[] tasks = new Task[functions.Length];
static void Main(string[] args)
{
for (int i = 0; i < functions.Length; ++i)
tasks[i] = Task.Run(() => { prices[i] = functions[i]("a string"); });
Task.WaitAll(tasks);
}
}
}
An eg. speaks a lot more than words.
I used it in a winform so the this refers to win form.
I have assumed all your methods are public , have same signature & return the same type.
enum MyName { AA,BB,CC};
//Call this in one of your methods
string [] strVal= Enum.GetNames(typeof(MyName));
int x = CallFunction(strVal[0], "A");
int y = CallFunction(strVal[1], "h");
int z = CallFunction(strVal[1], "C");
//End Call this in one of your methods
int CallFunction(string strName,string strValue)
{
return Convert.ToInt32(this.GetType().InvokeMember(strName, BindingFlags.Public | BindingFlags.InvokeMethod|BindingFlags.Instance, null, this, new object[] { strValue }));
}
public int AA(string s)
{
return 1;
}
public int BB(string s)
{
return 2;
}
public int CC(string s)
{
return 3;
}
Another solution. I hope somebody will consider it as overkill :)
Create abstract class DealerBase.
public abstract class DealerBase
{
public string Name { get; }
public decimal Price { get; set; }
protected DealerBase(string name)
{
Name = name;
}
public abstract void UpdatePrice();
}
Then create classes for every dealers you have and implement own logic for UpdatePrice method.
public class Dealer1 : DealerBase
{
public Dealer1() : base("DealerOne") { }
public override void UpdatePrice()
{
//Calculate price
Price = DealerOneCalculationMethod();
}
}
public class Dealer2 : DealerBase
{
public Dealer2() : base("DealerTwo") { }
public override void UpdatePrice()
{
//Calculate price
Price = DealerTwoCalculationMethod();
}
}
And so on..
Then you just create collection of dealers which can be easily iterated
var dealers = new List<DealerBase>
{
new Dealer1(),
new Dealer2()
}
foreach(var dealer in dealers)
{
dealer.UpdatePrice();
}
You can loop dealers and generate textboxes, labels in the winforms.
But I suggest to use DataGridView where code will be tiny clearer.
First implement INotifyPropertyChanged interface in the base class DealerBase
public abstract class DealerBase : INotifyPropertyChanged
{
public string Name { get; }
protected decimal _Price;
public decimal Price
{
get { return _Price; }
set
{
if (Equals(_Price, value)) return;
_Price = value;
// next method will inform DataGridView about changes
// and update value there too
RaisePropertyChanged();
}
protected DealerBase(string name)
{
Name = name;
}
public abstract void UpdatePrice();
// Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
The in the Form you can create BindingList<DealerViewModelBase> and set it to DataGridView.DataSource
public class YourForm: Form
{
public YourForm()
{
InitializeComponent();
var dealers = new List<DealerBase>
{
new Dealer1(),
new Dealer2()
};
var bindSource = new BindingList<DealerBase>(dealers);
dataGridView.DataSource = bindSource;
}
// Add button which update prices for all dealers
private void ButtonUpdatePrices_Click(object sender, EventArgs e)
{
var dealers = (BindingList<DealerBase>)dataGridView.DataSource;
foreach (var dealer in dealers)
{
dealer.UpdatePrice();
// Because we call `RaisePropertyChanged` in
// setter of Price - prices will be automatically
// updated in DataGridView
}
}
}
Idea of this approach you put different logic of different dealers in the separated class which. Because all dealer classes will inherit from same abstract class you can add different dealers to the collection.
You already have hardcoded enums and correspondent method which you try to link together. This approach make using of dealers collection little bid easy

encapsulation of an array of objects c#

I would like to create an array of objects. Each object has it's own int array.
For each object I assign values to it's array ONLY with keys given by myself (example: li[i].V[10] = 1; li[i].V[50] = 10; )
Can someone tell me how to do that? Can I do that without using Lists?
The second case is analogical to first. I would like to know how to assign values of object's List
using setter.
I tried to do that by myself. Unfortunately My code crashed cuz I don't know how to set the dimension of V and Word:
class CFiles
{
//private int[] v=new int[5];//dont want to specify the dimention of array here
private int[] v;//vector of file
private List<string> words;
public CFiles()
{
words = Words;
v = new int[50];
v = V;
}
public int[] V { get; set; }
public List<string> Words { get; set; }
}
class Program
{
static void Main(string[] args)
{
CFiles[] li = new CFiles[2];
for(int i=0;i<li.Length;i++)
{
li[i]=new CFiles();
li[i].V[10] = 1;
li[i].V[50] = 10;
li[i].V[50] = 15;
li[i].Words.Add("a");
li[i].Words.Add("ab");
li[i].Words.Add("abc");
}
for (int i = 0; i < li.Length; i++)
{
for(int j=0;j<li[i].V.Length;j++)
{
Console.WriteLine(li[i].V[j]);
}
}
Console.WriteLine();
}
}
Your constructor isn't right and your properties aren't quite right. You might want something more like this:
class CFiles
{
//private int[] v=new int[5];//dont want to specify the dimention of array here
private int[] v;
public int[] V { get { return v; } set { v = value; } }
private List<string> words;
public List<string> Words { get { return words; } set { words = value; } }
public CFiles()
{
words = new List<string>();
v = new int[51]; //needs to be 51 if you are going to assign to index 50 below
}
}
Other than those issues, your code seems to do what you want. You have an array of objects where each object has its own int array (in addition to a string of strings).
Is that not what you want?

Categories

Resources