I am trying to get the object value but I don't know how to do it. I'm new to C# and its giving me syntax error. I want to print it separately via the method "PrintSample" How can I just concatenate or append the whatData variable . Thank you.
PrintSample(getData, "name");
PrintSample(getData, "phone");
PrintSample(getData, "address");
//Reading the CSV file and put it in the object
string[] lines = File.ReadAllLines("sampleData.csv");
var list = new List<Sample>();
foreach (var line in lines)
{
var values = line.Split(',');
var sampleData = new Sample()
{
name = values[0],
phone = values[1],
address = values[2]
};
list.Add(sampleData);
}
public class Sample
{
public string name { get; set; }
public string phone { get; set; }
public string adress { get; set; }
}
//Method to call to print the Data
private static void PrintSample(Sample getData, string whatData)
{
//THis is where I'm having error, how can I just append the whatData to the x.?
Console.WriteLine( $"{getData. + whatData}");
}
In C# it's not possible to dynamically evaluate expression like
$"{getData. + whatData}"
As opposed to languages like JavaScript.
I'd suggest to use rather switch expression or Dictionary<string, string>
public void PrintData(Sample sample, string whatData)
{
var data = whatData switch
{
"name" => sample.name,
"phone" => sample.phone,
"address" => sample.address
_ => throw new ArgumentOutOfRangeException(nameof(whatData)),
};
Console.WriteLine(data);
}
I'm not sure what you are trying to achieve. Perhaps this will help you:
private static void PrintSample(Sample getData, string whatData)
{
var property = getData.GetType().GetProperty(whatData);
string value = (string)property?.GetValue(getData) ?? "";
Console.WriteLine($"{value}");
}
What PO really needs is
private static void PrintSamples(List<Sample> samples)
{
foreach (var sample in samples)
Console.WriteLine($"name : {sample.name} phone: {sample.phone} address: {sample.address} ");
}
and code
var list = new List<Sample>();
foreach (var line in lines)
{
......
}
PrintSamples(list);
it is radicolous to use
PrintSample(getData, "name");
instead of just
PrintSample(getData.name)
You can do this using reflection. However, it's known to be relatively slow.
public static void PrintSample(object getData, string whatData)
{
Console.WriteLine( $"{getData.GetType().GetProperty(whatData).GetValue(getData, null)}");
}
Related
Following my Is it possible to have a Function that takes any number of variables of any type?
I have the function that gets any number of any type of variables and it works perfectly
public string funcVars(params object[] paths)
{
string strVars = String.Join(", ", paths.Select(x => x.ToString()));
return strVars;
}
To call it I'd simply need to
string someString ="asd"; int someInt = 123; bool someBool=false;
funcVars(someString,someInt,someBool);
And the output would be
asd,123,false
is there any simple way I can also get the variable names as well as their values, so the output would be
asd,123,false,someString,someInt,someBool //(or any other similar form)
Or do I need to hardcode the names every time I call my method ?
funcVars("someString","someInt","someBool",someString,someInt,someBool);
What you really should be doing is creating a class to hold your variables:
internal class MyValues
{
internal string SomeString { get; set; }
internal int SomeInt { get; set; }
internal bool SomeBool { get; set; }
}
Then you can pass an instance of your class:
var mv = new MyValues() { SomeString = "asd", SomeInt = 123, SomeBool = false };
funcVars(mv);
Here is funcVars:
public string funcVars(MyValues values)
{
string strVars =
String.Join(", ", new[] { values.SomeString,
values.SomeInt.ToString(), values.SomeBool.ToString() });
return strVars;
}
Straight up stealing roy.ap's code and adding the "nameof()" method since getting the name of the property seemed to be apart of the question.
class Program
{
internal class MyValues
{
internal string SomeString { get; set; }
internal int SomeInt { get; set; }
internal bool SomeBool { get; set; }
}
static void Main(string[] args)
{
var mv = new MyValues() { SomeString = "asd", SomeInt = 123, SomeBool = false };
Console.WriteLine(funcVars(mv));
Console.ReadLine();
}
public static string funcVars(MyValues values)
{
string strVars =
String.Join(", ", new[]
{
nameof(values.SomeString), values.SomeString,
nameof(values.SomeInt), values.SomeInt.ToString(),
nameof(values.SomeBool), values.SomeBool.ToString()
});
return strVars;
}
}
There really isn't a way to get the variable names via the the function itself because the scope changes once you're in the method. That is even if you pass an array of objects, if you perform a foreach to go through each object you will give the individual objects a new scope specific name.
No, because the variables are not actually passed
No it is not possible, because the variables themselves are not actually passed. Their values are passed.
Consider this code:
string someString ="asd"; int someInt = 123; bool someBool=false;
funcVars(someString,someInt,someBool);
In your call to funcVars, all the parameters are passed by value. All three variables are copied, and copy of them is put on the stack. These stack variables are identified by completely different symbols-- (e.g. paths[0],paths[1], etc.)
After all, what would happen if you called it like this?
funcVars("Hello",245+25,test != null);
Obviously those values do not have variable names. There is no way your function can possibly retrieve what doesn't exist.
Use ExpandoObject instead
The System.Dynamic.ExpandoObject seems like a really good fit for this problem.
var args = new System.Dynamic.ExpandoObject();
args.SomeString = "hello";
args.SomeInt = 32;
args.SomeBool = false;
funcVars(args);
public static string funcVars(ExpandoObject inputs)
{
var sb = new StringBuilder();
foreach (KeyValuePair<string, object> kvp in inputs)
{
sb.Append(String.Format("{0} = {1}", kvp.Key, kvp.Value);
}
return sb.ToString();
}
Is this bad practice? Or is it completely fine to do this
private readonly List<KeyValuePair<GameType, KeyValuePair<string, string>>> _tempStats = new List<KeyValuePair<GameType, KeyValuePair<string, string>>>();
GameType is an enumerator by the way.
I have data that I am downloading from a table that has a few different GameTypes with two strings associated with them. So it will parse the data and determines what GameType to assign it, and it finds the tables Key and it's Value. And it works, it stores the information and I am able to retrieve it with no problems, but it just seems like having a list of a KeyValuePair with a KeyValuePair isn't right, but maybe it is. Would using a tuple be a better approach?
My current usage of the list
private void ParseNodeText(string nText, GameType gmode)
{
_tempStats.Clear();
var reader = new StringReader(nText);
while((nText = reader.ReadLine()) != null)
{
nText = nText.Replace(" ", "");
if (nText == "")
{
continue;
}
string statType = Regex.Replace(nText, "[^A-Za-z]", "");
string statValue = Regex.Replace(nText, "[^0-9]", "");
// Console.WriteLine(gmode + " : Found line with Type of {0} and a value of {1}",statType,statValue);
_tempStats.Add(new KeyValuePair<GameType, KeyValuePair<string, string>>(gmode, new KeyValuePair<string, string>(statType, statValue)));
}
}
Your Solution is fine, but not that good readable.
You could do something like this:
private readonly List<GameStats> _tempStats = new List<GameStats>();
and GameStats is a own written simple Class:
public class GameStats
{
public GameType Type { get; set; }
public string StatType { get; set; }
public string StatValue { get; set; }
public GameStats(GameType gameType, string statType, string statValue)
{
this.Type = gameType;
this.StatType = statType;
this.StatValue = statValue;
}
}
and add it to your list like:
_tempStats.Add(new GameStats(gmode, statType, statValue));
i think, this looks good and will do it.
I want to use a foreach k, v pairs loop to run through a multidimensional list, and output those values elsewhere. Here is the code:
public class LogTable
{
public string FunctionName { get; set; }
public string LogTime { get; set; }
}
public class TableVars
{
public List<LogTable> loggingTable { get; set; }
public TableVars()
{
loggingTable = new List<LogTable>();
}
public void createLogList()
{
loggingTable = new List<LogTable>();
}
}
foreach( KeyValuePair<string, string> kvp in tablevars.loggingTable)
{
// output would go here. I haven't looked it up yet but I assume it's something
// along the lines of var = k var2 = v? Please correct me if I'm wrong.
}
When I run my mouse over 'foreach' I get a warning saying - 'Cannot convert type 'DataObjects.LogTable' to 'System.Collections.Generic.KeyValuePair. How can I resolve this issue, or is there a more efficient way to accomplish the same goal?
Thanks!
I should have added more context, sorry. I'm trying to return the two different values inside the properties 'FunctionName' and 'LogTime' which I have added via:
var tablevars = new TableVars();
tablevars.loggingTable.Add(new LogTable { FunctionName = errorvalue, LogTime = logtime });
To specify more accurately, the intention of the foreach k, v loop was to grab every distinct property of FunctionName and LogTime and input them into a database in SQL Server. This is why the distinction between k, v (or FunctionName, LogTime) is important. Again, please correct me if I'm wrong.
You cannot use KeyValuePair<>, because you don't enumerate a Dictionary<>. But you don't need to, simply do this:
foreach(LogTable logTable in tablevars.loggingTable)
{
// do whatever with logTable.FunctionName and logTable.LogTime
}
Either change your foreach to iterate through List<LogTable>
foreach( LogTable lt in tablevars.loggingTable)
{...}
OR
Use a KeyValuePair instead of creating class LogTable
public class TableVars
{
public Dictionary<string,string> loggingTable { get; set; }
public TableVars()
{
loggingTable = new Dictionary<string,string>();
}
public void createLogList()
{
loggingTable = new Dictionary<string, string>();
}
}
foreach( KeyValuePair<string, string> kvp in tablevars.loggingTable)
{
//loggingTagle is now a KeyValuePair
}
I have this class:
public class allFields
{
public string EAN { get; set; }
public string title { get; set; }
public string qty { get; set; }
public string price { get; set; }
public DateTime date { get; set; }
}
And a function that return an anonymous type:
public IEnumerable<object> stockEtatQty()
{
List<allFields> afList = new List<allFields>();
var query = from x in ctx.book
where x.qty > 0
select x;
foreach (var item in query)
{
allFields af = new allFields();
af.EAN = item.EAN;
af.title = item.Titre;
af.qty = ""+item.Quantite;
afList.Add(af);
}
var q = from x in afList
select new { EAN=x.EAN, Title=x.title, Quantity=x.qty };
return q; //q is a IEnumerable<'a> where a is new {string EAN, string Title, string Quantity}
}
In my WinForm a use this function as below:
private void QuantityToolStripMenuItem_Click(object sender, EventArgs e)
{
ServiceStock sstock = new ServiceStock();
var q = sstock.stockEtatQty().ToList();// q is a list<object>
string str = "";
foreach (var item in q)
{
str += item + Environment.NewLine;
}
MessageBox.Show(str);
}
The result is:
{ EAN = 1, Title = CSharp Security, Quantity = 970 }
{ EAN = 2, Title = MISC, Quantity = 100 }
...
What I want?
I want not like the result above, but separate each field apart of the item in the loop foreach, e.g get item.EAN, item.Title and item.Quantity.
If there is no solution for my problem I would like to know an alternative,
Thanks for help.
The obvious solution is to create a custom type (let's call it BookInfo) and return a IEnumerable<BookInfo> instead of a IEnumerable<object> (and maybe override ToString if you want to put the formatting into this class itself).
Then you can easily format the output.
public class BookInfo
{
public string EAN {get;set;}
public string Title {get;set;}
public int Quantity {get;set;}
}
public IEnumerable<BookInfo> stockEtatQty()
{
...
var q = from x in afList
select new BookInfo { EAN=x.EAN, Title=x.title, Quantity=x.qty };
return q;
}
private void QuantityToolStripMenuItem_Click(object sender, EventArgs e)
{
ServiceStock sstock = new ServiceStock();
var q = sstock.stockEtatQty();
var message = string.Join(Environment.NewLine,
q.Select(item => String.Format("{0} {1} {2}", item.EAN, item.Title, item.Quantity)));
MessageBox.Show(message);
}
Since the static type information about the object of anonymous type is lost by the time that you exit stockEtatQty() method, you could cast the object to dynamic and access fields like this:
str = string.Join(Environment.NewLine, q.Cast<dynamic>().Select(item =>
string.Format("{0} {1} {2}", item.EAN, item.Title, item.Quantity)
));
The cast to dynamic tells the compiler that EAN, Title, and Quantity need to be resolved at runtime.
Note that I also replaced the foreach loop with a call to string.Join to improve performance: repeated string concatenation creates unnecessary partial string objects, which string.Join avoids. Another solution would be to use StringBuider instead of string concatenation +=.
stockEtatQty is in a project (Service) and QuantityToolStripMenuItem_Click is in another project (View)
Unfortunately, this means that you would not be able to use anonymous types: anonymous types are generated with internal visibility, limiting their use to the assembly in which they are produced. You can use a work-around based on ExpandoObject described in this answer:
var q = afList.Select(x => {
dynamic res = new ExpandoObject();
res.EAN=x.EAN;
res.Title=x.title;
res.Quantity=x.qty;
return res;
});
Create a new class that represents the new object structure and return that.
var q = from x in afList
select new SmallerType { EAN=x.EAN, Title=x.title, Quantity=x.qty };
WinForm Function
foreach (SmallerType item in q)
{
//
}
You can use collection of dynamic objects instead of simple objects as return type of your method:
public IEnumerable<dynamic> stockEtatQty()
Then you will not have IntelliSense but at runtime properties will be found:
foreach (var item in sstock.stockEtatQty())
str += String.Format("{0}", item.EAN) + Environment.NewLine;
But I suggest you to create custom class with EAN, Title and Quantity properties. Or just use your allFields instead of anonymous objects.
Consider also to use StringBuilder for string creation to avoid creating lot of in-memory strings:
var builder = new StringBuilder();
foreach (var item in sstock.stockEtatQty())
builder.AppendFormat("{0}{1}", item.EAN, Environment.NewLine);
MessageBox.Show(builder.ToString());
What is the C# equivalent of this pseudo-code?
var pattern = ...;
var lookup = new Dictionary<string, string>();
foreach (var group in new Regex(pattern).Matches())
{
lookup[group.Name] = group.Value;
}
I don't see any System.Text.RegularExpressions group-related object that exposes the group name.
What am I missing?
What I'm actually trying to do is convert a file with lines in this format:
eventName|message|date
To an IEnumerable<EventLogLine>, with EventLogLine being:
public struct EventLogLine
{
public string EventName { get; set; }
public string Message { get; set; }
public DateTime Date { get; set; }
}
And put those lines into a IDictionary<string /*EventName*/, IEnumerable<EventLogLine>>.
I just knocked this up in using LINQ. It relies on the List<string> to be filled with the lines in the file.
var lines = new List<string>();
var dict = lines.Select(l =>
{
var sp = l.Split('|');
return new EventLogLine { EventName = sp[0], Message = sp[1], Date = DateTime.Parse(sp[2]) };
})
.GroupBy(e => e.EventName)
.ToDictionary(grp => grp.Key, grp => grp.AsEnumerable());
Basically you convert each line to an EventLogLine, using the Select(), then use the GroupBy() to create your grouping based on EventName, then using the ToDictionary() to run the query and create your dictionary in the format required!
See the example in the Match.Groups MSDN article. I think you should look at Alastair's answer though, seeing as your input is so simple it would probably be easier to read the code later if you just use ReadLine and Split.
Consider using ToLookup rather than ToDictionary. Lookups work naturally with linq and generic code in general by being immutable and by exposing a a very simple API. Also, I would encapsulate the parsing into the EventLogLine struct.
As a result, the code would look like this:
IEnumerable<string> lines;
ILookup<string, EventLogLine> lookup =
lines.Select(EventLogLine.Parse).ToLookup(evtLine => evtLine.EventName);
An example consumer:
if(lookup["HorribleEvent"].Any())
Console.WriteLine("OMG, Horrible!");
foreach(var evt in lookup["FixableEvent"])
FixIt(evt);
var q = from evtName in relevantEventNames
from evt in lookup[evtName]
select MyProjection(evt);
Note that you do not need to check for key-existance, unlike for a Dictionary:
if(dictionary.ContainsKey("HorribleEvent")) //&& dictionary["HorribleEvent"].Any() sometimes needed
Console.WriteLine("OMG, Horrible!");
if(dictionary.ContainsKey("FixableEvent"))
foreach(var evt in lookup["FixableEvent"])
FixIt(evt);
var q = from evtName in relevantEventNames.Where(dictionary.ContainsKey)
from evt in dictionary[evtName]
select MyProjection(evt);
As you may notice, working with a dictionary containing IEnumerable values introduces subtle friction - ILookup is what you want!
Finally, the modified EventLogLine:
public struct EventLogLine {
public string EventName { get; private set; }
public string Message { get; private set; }
public DateTime Date { get; private set; }
public static EventLogLine Parse(string line) {
var splitline = line.Split('|');
if(splitline.Length != 3) throw new ArgumentException("Invalid event log line");
return new EventLogLine {
EventName = splitline[0],
Message = splitline[1],
Date = DateTime.Parse(splitline[2]),
};
}
}
To answer this part of your question:
I don't see any
System.Text.RegularExpressions
group-related object that exposes the
group name. What am I missing?
I have adapted Eamon Nerbonne's struct to use regular expressions:
public struct EventLogLine
{
public string EventName { get; private set; }
public string Message { get; private set; }
public DateTime Date { get; private set; }
private static Regex expectedLineFormat = new Regex(
#"^(?<eventName>[^|]*)\|(?<message>[^|]*)\|(?<date>[^|]*)$",
RegexOptions.Singleline | RegexOptions.Compiled
);
public static EventLogLine Parse(string line) {
Match match = expectedLineFormat.Match(line);
if (match.Success) {
return new EventLogLine {
EventName = match.Groups["eventName"].ToString(),
Message = match.Groups["message"].ToString(),
Date = DateTime.Parse(match.Groups["date"].ToString()
};
}
else {
throw new ArgumentException("Invalid event log line");
}
}
}
To more directly answer your original question (without commenting on your approach), as I had a similar problem...
According to Mono source code, the enumeration for the Groups indexer is based on the private Match.regex field, so you'll need to still have the Regex. But if you do, like you had above...
public static Dictionary<string, string> ToDictionary(
Regex regex, GroupCollection groups)
{
var groupDict = new Dictionary<string, string>();
foreach (string name in regex.GetGroupNames()){ //the only way to get the names
Group namedGroup = groups[name]; //test for existence
if (namedGroup.Success)
groupDict.Add(name, namedGroup.Value);
}
return groupDict;
}
or, as Linq,
regex.GetGroupNames()
.Where(name => groups[name].Success)
.ToDictionary(name => name, name => groups[name].Value)