Can we create dynamic variable in C#?
I know my below code is threw error and very poor coding. But this code have small logic like create dynamic variable
var name=0;
for(i=0;i<10;i++)// 10 means grid length
{
name+i=i;
}
var xx1=name1;
var xx2=name2;
var xx3=name3;
Is it possible in c#? Create dynamic variable in c#? and change the variable name in c#? and concatenate the variable name in c#(like we can concatenate any control id or name)...
Why I need the dynamic variable name (scenario):
var variablename=""
var variablename0=No;
var variablename1=Yes;
var variablename2=No;
.
.
.
I have a gridview with multiple rows. And I need assign server side variable to every row. So I need set of variables in server side. the only I can set Text=<%# variablename+rowCount%> for every template field.
This rowCount means every grid row index.
If the grid has 2 rows, Then rowCount values are 0,1,2
Now I need to change the variablename to variablename0,variablename1,variablename2 dynamically for separate row.
C# is strongly typed so you can't create variables dynamically. You could use an array but a better C# way would be to use a Dictionary as follows. More on C# dictionaries here.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QuickTest
{
class Program
{
static void Main(string[] args)
{
Dictionary<string, int> names = new Dictionary<string,int>();
for (int i = 0; i < 10; i++)
{
names.Add(String.Format("name{0}", i.ToString()), i);
}
var xx1 = names["name1"];
var xx2 = names["name2"];
var xx3 = names["name3"];
}
}
}
No. That is not possible. You should use an array instead:
name[i] = i;
In this case, your name+i is name[i].
Variable names should be known at compile time. If you intend to populate those names dynamically at runtime you could use a List<T>
var variables = List<Variable>();
variables.Add(new Variable { Name = inputStr1 });
variables.Add(new Variable { Name = inputStr2 });
here input string maybe any text or any list
try this one, user json to serialize and deserialize:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
object newobj = new object();
for (int i = 0; i < 10; i++)
{
List<int> temp = new List<int>();
temp.Add(i);
temp.Add(i + 1);
newobj = newobj.AddNewField("item_" + i.ToString(), temp.ToArray());
}
}
}
public static class DynamicExtention
{
public static object AddNewField(this object obj, string key, object value)
{
JavaScriptSerializer js = new JavaScriptSerializer();
string data = js.Serialize(obj);
string newPrametr = "\"" + key + "\":" + js.Serialize(value);
if (data.Length == 2)
{
data = data.Insert(1, newPrametr);
}
else
{
data = data.Insert(data.Length-1, ","+newPrametr);
}
return js.DeserializeObject(data);
}
}
}
This is not possible, it will give you a compile time error,
You can use array for this type of requirement .
For your Reference :
http://msdn.microsoft.com/en-us/library/aa288453%28v=vs.71%29.aspx
Related
I have the following json string
[{"field1":"1","field2":"2","field3":"3"}{"field1":"11","field2":"22","field3":"33"}{"field1":"111","field2":"222","field3":"333"}]
I use the following code to parse it -
var jss = new JavaScriptSerializer();
dynamic data = jss.Deserialize<dynamic>(s);
How can I iterate the dynamic data array ?
using System.IO;
using System.Web.Script.Serialization;
namespace Deserialize
{
class Program
{
static void Main()
{
string jsonString = File.ReadAllText("dynamic.json");
var serializer = new JavaScriptSerializer();
dynamic data = serializer.Deserialize<dynamic>(jsonString);
foreach (var item in data)
{
foreach (var subitem in item)
{
System.Console.WriteLine("Key={0}, Value={1}", subitem.Key, subitem.Value);
}
System.Console.WriteLine();
}
System.Console.ReadKey();
}
}
}
I need to add commas to separate the 3 objects in the json file for this to work ... but I think this is (roughly) what you are after. Sorry if I have misunderstood.
I ended up using the following code.
List<myClass> l = JsonConvert.DeserializeObject<List<myClass>>(jsonString);
Than I had no problem iterate through l.
myClass is a local class with the field definition, need to make sure field and class defined public.
I am currently working on a project that involves taking a text file with information in it and storing the values into an array for use in determining if a certain book should be "split" based on its ID.
I have declared a string array in the class that is executing the method for this task, and assigning the values from the text file using a StreamReader.
Here is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.Odbc;
using System.Data.OleDb;
using System.IO;
namespace ElectionsPollBooks
{
class dbElections
{
//arrays, ints, for pollbook splits
string[] as_splitNumbers;
int i_splitCount;
public void Process()
{
//opens conenction
OpenConn();
//Gets the precinct info for later parsing
GetDistrictInfo();
//populate splits array
PopulateSplits();
//the hard work
SeperateDataSet();
CloseConn();
}
//...other methods in here, not related
private void PopulateSplits()
{
//sets the count
i_splitCount = 0;
//reads the split file
StreamReader sr_splits = new StreamReader(#"a\file\path\here\.txt");
//begin populating the array
while (sr_splits.ReadLine() != null)
{
//split ID
as_splitNumbers[i_splitCount] = sr_splits.ReadLine();
i_splitCount = i_splitCount + 1;
}
sr_splits.Close();
sr_splits.Dispose();
}
}
}
Visual Studio is telling me at this line:
string[] as_splitNumbers;
That:
"as_splitNumbers is never assigned to and will always return a null value."
When I also run the program, it throws a NullReferenceException during the while loop.
My question is then, what am I doing wrong when it comes to assigning the StreamReader values to the as_splitNumbers array? What am I missing in my logic?
Thank you.
You're not initializing your array with a size.
What you could do if you don't know the size is use List<int>.
Change
string[] as_splitNumbers
to
List<string> as_SplitNumbers = new List<string>();
and your method to:
private void PopulateSplits()
{
//sets the count
i_splitCount = 0;
//reads the split file
using(StreamReader sr_splits = new StreamReader(#"a\file\path\here\.txt"))
{
//begin populating the array
while (sr_splits.ReadLine() != null)
{
//split ID
string split = sr_splits.ReadLine();
as_splitNumbers.Add(split);
i_splitCount = i_splitCount + 1;
}
}
}
If what you're sending it to (SeperateDataSet();?) requires an array, you can cast it by using _asSplitNumbers.ToArray() later on. List<T> just allows you to add without knowing the size.
Try to use a List (System.Enumerable).
Because you donn't know the size of the array before reading.
At the declaration of the variable it will means:
List<string> as_splitNumbers = new List<string>();
in the loop you can simply write
as_splitNumbers.Add(sr_splits.ReadLine())
and it will work!
Your as_splitNumbers array is never allocated. You need to initialize the array with a size first.
string[] as_splitNumbers = new string[SIZE];
However, it seems you should just use a List in your case.
List<string> as_splitNumbers = new List<string>();
Then
//split ID
as_splitNumbers.Add(sr_splits.ReadLine());
I have the following Dictionary:
Dictionary<string, List<string>> myList = new Dictionary<string,List<string>>();
This dictionary gets populated, I'm never going to know how many items I will have.
An example of the output:
Elements of Dictionary: (Key,Value)
{"Code",{"Test1", "Test2", "Test3"}}
{"Desc",{"Desc1", "Desc2", "Desc3"}}
{"Name",{"name1", "name2", "name3"}}
How can I loop through every dictionary and get the value i.e. by index
something that would yield ->
{Code = "Test1", Desc = "desc", Name = "name1"}
{Code = "Test2", Desc = "desc2", Name = "name2"}
{Code = "Test3", Desc = "desc3", Name = "name3"}
Any ideas?
Thanks
The key is to build a set of arrays for each column of your data instead of for each rows like the Dictionary enumerator will provide you. This can be achieved through the use of the Aggregate extension method and a simple Select statement.
// Assuming the following class as a destination type
class Foo
{
public Foo(string[] values)
{
Code = values[0];
Name = values[1];
Desc = values[2];
}
public string Code;
public string Name;
public string Desc;
}
// This would be the code required to parse the data
var destination = dataSource["Code"].Aggregate(new List<Foo>(), (entries, _) =>
{
var curentRow = entries.Count();
var entryData = dataSource.Select(property => property.Value[curentRow]).ToArray();
entries.Add(new Foo(entryData));
return entries;
});
In this case, we use the Code property as a key to figure out how many entries there are in your data source (your dictionary). If there are rows in your dictionary with missing values (less items than in the Code row), this code will fail as it assumes that there are the same amount of items in all the rows.
The Aggregate method acts like a for loop in this case, providing us with a basic counter named currentRow that we will use later to access specific entries in your data. This counter is the amount of entries we stored into the List<Foo>. It starts at 0 and it increments each time we add a new value to the result set.
The next step is to look at all the entries in your data source and to access the value that matches the current row. We then convert it into an array and feeds it into the constructor of your destination type that knows how to deserialize this data.
This is a basic solution which perfectly solves your problem. I have created an IEnumerable object so that you can increment foreach loop by "Code","Desc",... .
Once you get the strings( which are Keys of Dictionary ) you can interate through the list that is the value of each key in the dictionary. The code below Writes and counts all elements in Dictionary and for each Values writes all elements in Lists.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
public static IEnumerable<string> Helper() // Neccessary to use in foreach loop
{
yield return "Code";
yield return "Desc";
yield break;
}
static void Main(string[] args)
{
Dictionary<string, List<string>> container = new Dictionary<string, List<string>>();
List<string> l1 = new List<string>();
List<string> l2 = new List<string>();
l1.Add("l1s1");
l1.Add("l1s2");
l1.Add("l1s3");
l2.Add("l2s1");
l2.Add("l2s2");
l2.Add("l2s3");
container.Add("Code", l1);
container.Add("Desc", l2);
int count = 0;
foreach (string k in Helper()) // get all Keys
{
for (int i = 0; i < container[k].Count; i++)
{
Console.WriteLine(container[k][i].ToString()); // Write each element in list
count++;
}
Console.WriteLine();
}
Console.WriteLine(count.ToString());
}
}
}
Note that you don't need to know how many elements will be in each list.Output is like :
Or without creating Helper() you can do like below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Dictionary<string, List<string>> container = new Dictionary<string, List<string>>();
List<string> l1 = new List<string>();
List<string> l2 = new List<string>();
l1.Add("l1s1");
l1.Add("l1s2");
l1.Add("l1s3");
l2.Add("l2s1");
l2.Add("l2s2");
l2.Add("l2s3");
container.Add("Code", l1);
container.Add("Desc", l2);
int count = 0;
foreach (string k in new string [] {"Code","Desc"}) // get all Keys
{
for (int i = 0; i < container[k].Count; i++)
{
Console.WriteLine(container[k][i].ToString()); // Write each element in list
count++;
}
Console.WriteLine();
}
Console.WriteLine(count.ToString());
}
}
}
looks like the wrong data structure. Seems like you have a set of objects each with an arbitrary set of attributes. You need map<string,map<string,string>>
"item1"->code:'code1",desc:"desc1"....
"item2"->code:'code2",desc:"desc2", color:"red", size:'42"
......
dynamic model = new ExpandoObject();
model.Data = "asdf";
List<dynamic> listOfx = new List<dynamic>();
for (int i = 0; i < 3; i++) {
dynamic x = new ExpandoObject();
x.ID = i;
x.Name = "test" + i.ToString();
listOfx.Add(x);
}
model.listOfx = listOfx;
When I run this, I can see Data inside model, but not listOfx.
Problem: how to get a list(or IEnumerable) inside an ExpandoObject
UPDATE on Solution:
Because I couldn't see the lifOfx in the locals window I thought it wasn't working. Here (through y) you can see it is. :-)
I can't reproduce similar issues on Mono 2.10:
using System.Dynamic;
using System.Collections.Generic;
using System;
public class Program
{
public static void Main(string[] args)
{
dynamic x = new ExpandoObject();
x.Data ="test";
x.Arr = new [] { "test1","test2"};
x.Lst = new List<string> { "aap", "noot", "mies" };
Console.WriteLine(string.Join(", ", x.Arr));
Console.WriteLine(string.Join(", ", x.Lst));
}
}
Output:
/tmp # dmcs test.cs && mono test.exe
test1, test2
aap, noot, mies
I'll be retesting on windows shortly.
Update have tested the following:
the linux-compiled (dmcs) binary run on Windows with Mono 2.10: OK
the linux-compiled (dmcs) binary run on Windows with MS.NET 4.0: OK
the windows-compiled (dmcs) binary run on Windows with Mono 2.10: OK
the windows-compiled (dmcs) binary run on Windows with MS.NET 4.0: OK
the windows-compiled (csc.exe) binary run on Windows with Mono 2.10: OK
the windows-compiled (csc.exe) binary run on Windows with MS.NET 4.0: OK
On linux I have only tested the binary compiled by mono itself, but I don't anticipate any problems. Perhaps there is something subtly different about storing dynamics inside the List<>, I'll test that now
The code you have, above, works perfectly well for setting up the list. For example, adding this after your code will work fine:
// Access value inside list
Console.WriteLine(model.listOfx[1].Name);
// Iterate through list
foreach (var o in model.listOfx)
{
Console.WriteLine(o.ID);
}
For example, try the following (fully functional example):
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
public static class Test
{
public static void Main()
{
dynamic model = new ExpandoObject();
model.Data = "asdf";
List<dynamic> listOfx = new List<dynamic>();
for (int i = 0; i < 3; i++)
{
dynamic x = new ExpandoObject();
x.ID = i;
x.Name = "test" + i.ToString();
listOfx.Add(x);
}
model.listOfx = listOfx;
// Access value inside list
Console.WriteLine(model.listOfx[1].Name);
// Iterate through list
foreach (var o in model.listOfx)
{
Console.WriteLine(o.ID);
}
Console.ReadKey();
}
}
This uses your exact sample code.
The ExpandoObject supports IDictionary. You can cast it such as
IDictionary myModel =(IDictionary)Model;
So then you can iterate it.
Why is my delimiter not appearing in the final output? It's initialized to be a comma, but I only get ~5 white spaces between each attribute using:
SELECT [article_id]
, dbo.GROUP_CONCAT(0, t.tag_name, ',') AS col
FROM [AdventureWorks].[dbo].[ARTICLE_TAG_XREF] atx
JOIN [AdventureWorks].[dbo].[TAGS] t ON t.tag_id = atx.tag_id
GROUP BY article_id
The bit for DISTINCT works fine, but it operates within the Accumulate scope...
Output:
article_id | col
-------------------------------------------------
1 | a a b c
Update: The excess space between values is because the column as defined as NCHAR(10), so 10 characters would appear in the output. Silly mistake on my part...
Solution
With Martin Smith's help about working with the Write(BinaryWriter w) method, this update works for me:
public void Write(BinaryWriter w)
{
w.Write(list.Count);
for (int i = 0; i < list.Count; i++ )
{
if (i < list.Count - 1)
{
w.Write(list[i].ToString() + delimiter);
}
else
{
w.Write(list[i].ToString());
}
}
}
The Question:
Why does the above solve my problem? And why wouldn't it let me use more than one w.write call inside the FOR loop?
C# Code:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Xml.Serialization;
using System.Xml;
using System.IO;
using System.Collections;
using System.Text;
[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined, MaxByteSize = 8000)]
public struct GROUP_CONCAT : IBinarySerialize
{
ArrayList list;
string delimiter;
public void Init()
{
list = new ArrayList();
delimiter = ",";
}
public void Accumulate(SqlBoolean isDistinct, SqlString Value, SqlString separator)
{
delimiter = (separator.IsNull) ? "," : separator.Value ;
if (!Value.IsNull)
{
if (isDistinct)
{
if (!list.Contains(Value.Value))
{
list.Add(Value.Value);
}
}
else
{
list.Add(Value.Value);
}
}
}
public void Merge(GROUP_CONCAT Group)
{
list.AddRange(Group.list);
}
public SqlString Terminate()
{
string[] strings = new string[list.Count];
for (int i = 0; i < list.Count; i++)
{
strings[i] = list[i].ToString();
}
return new SqlString(string.Join(delimiter, strings));
}
#region IBinarySerialize Members
public void Read(BinaryReader r)
{
int itemCount = r.ReadInt32();
list = new ArrayList(itemCount);
for (int i = 0; i < itemCount; i++)
{
this.list.Add(r.ReadString());
}
}
public void Write(BinaryWriter w)
{
w.Write(list.Count);
foreach (string s in list)
{
w.Write(s);
}
}
#endregion
}
The problem here is that you do not serialize delimiter. Add:
w.Write(delimiter)
as a first line in your Write method and
delimiter = r.ReadString();
as a first line in your Read method.
Regarding your questions to suggested work-around:
Why does the above solve my problem?
It does not. It merely worked with your test scenario.
And why wouldn't it let me use more than one w.write call inside the FOR loop?
Write method needs to be compatible with Read method. If you write two strings and read only one then it is not going to work. The idea here is that your object may be removed from the memory and then loaded. This is what Write and Read are supposed to do. In your case - this indeed was happening and you were not able to keep the object value.
The answer given by #agsamek is correct but not complete. The query processor may instantiate multiple aggregators, e.g. for parallel computations, and the one that will eventually hold all data after successive calls of Merge() may be assigned an empty recordset, i.e. its Accumulate() method may be never called:
var concat1 = new GROUP_CONCAT();
concat1.Init();
results = getPartialResults(1); // no records returned
foreach (var result in results)
concat1.Accumulate(result[0], delimiter); // never called
...
var concat2 = new GROUP_CONCAT();
concat2.Init();
results = getPartialResults(2);
foreach (var result in results)
concat2.Accumulate(result[0], delimiter);
...
concat1.Merge(concat2);
...
result = concat1.Terminate();
In this scenario, concat1's private field delimiter used in Terminate() remains what it is by default in Init() but not what you pass in SQL. Luckily or not, your test SQL uses the same delimiter value as in Init(), so you can't reveal the difference.
I'm not sure if this is a bug or if it has been fixed in later versions (I stumbled on it in SQL Server 2008 R2). My workaround was to make use of the other group that is passed in Merge():
public void Merge(GROUP_CONCAT Group)
{
if (Group.list.Count != 0) // Group's Accumulate() has been called at least once
{
if (list.Count == 0) // this Accumulate() has not been called
delimiter = Group.delimiter;
list.AddRange(Group.list);
}
}
P.S. I would use StringBuilder instead of ArrayList.