How to get parameter name with C# - c#

How can we get parameter name called by a method in C#?
Example:
public static void PrintList (List<string> list)
{
Console.WriteLine("\n");
foreach (var item in list)
{
Console.WriteLine(item);
}
Console.WriteLine("\n");
}
PrintList(oxygenList);
I need the method to print:
oxygenList
Thanks.

If you're using C# 10 or later, you can use the new CallerArgumentExpression attribute to achieve this:
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
public static class Program
{
public static void Main()
{
List<string> oxygenList = new List<string> { "A", "B", "C" };
PrintList(oxygenList);
}
public static void PrintList(List<string> list, [CallerArgumentExpression("list")] string? name = null)
{
Console.WriteLine("Argument name = " + name); // Prints "Argument name = oxygenList
Console.WriteLine("\n");
foreach (var item in list)
{
Console.WriteLine(item);
}
Console.WriteLine("\n");
}
}
However, note that this gives the expression used when calling the method - so if you call it with this code:
public static void Main()
{
PrintList(getOxygenList());
}
public static List<string> getOxygenList()
{
return new List<string> { "A", "B", "C" };
}
the value passed as name will be "getOxygenList()".
It has to work like this because an expression can be used for the parameter - it's not restricted to a simple variable name.

You should use:
Console.WriteLine(nameof(list));
See more: nameof
Update:
This is still not clear for me what do You want to achieve but the easiest way would be:
public static void PrintList (List<string> list, string nameOfList)
{
Console.WriteLine(nameOfList);
Console.WriteLine("\n");
foreach (var item in list)
{
Console.WriteLine(item);
}
Console.WriteLine("\n");
}
PrintList(oxygenList, nameof(oxygenList));
You may also create a bit cleaner extension method, like this:
public static class ListPrinter
{
public static void PrintListWithName(this List<string> list, string nameOfList)
{
Console.WriteLine(nameOfList);
list.ForEach(element => Console.WriteLine(element));
}
}
called like this:
oxygenList.PrintListWithName(nameof(oxygenList));

Related

How to pass a list and string into a generic method which has generic list parameter

I have the following method
private static void WriteReport<T>(List<T> report, string reportName)
{
using (StreamWriter sw = new StreamWriter(#path)
{
for (var row = 0; row < report.Count; row++)
{
for (var column = 0; column < report.ElementAt(row).Count; column++)
sw.Write(report[row][column] + ",");
sw.WriteLine();
}
}
}
I can only pass in the above method the List<object> as parameter but I also want to pass parameter List<List<object>> in some cases. But I couldn't figure out the correct way to do so.
Edit : Cause whenever I pass List<List<object>> only then the nested for loop can be applied but if I pass list<object> then no nested loop can be applied and not sure how to structure my method to code it correctly
I wanted to know how to write a generic method to do so and also a bit curious to see if the generic method improves code maintainability and readability as well
You are not be able to convert T in List from T again, the solution that i see is to overload the method like this:
public static void Main()
{
WriteReport(new List<List<string>>()
{
new List<string>()
{
"Some Item",
"Onather Item"
},
new List<string>()
{
"Test Item",
"Demo Item"
}
}, "Report Name");
}
private static void WriteReport<T>(List<T> report, string reportName)
{
foreach (var item in report)
{
Console.WriteLine(item);
}
}
private static void WriteReport<T>(List<List<T>> report, string reportName)
{
foreach (var item in report)
{
WriteReport(item, reportName);
}
}
private static void WriteReport<T>(List<List<List<T>>> report, string reportName)
{
foreach (var item in report)
{
WriteReport(item, reportName);
}
}
private static void WriteReport<T>(List<List<List<List<T>>>> report, string reportName)
{
foreach (var item in report)
{
WriteReport(item, reportName);
}
}

Am I doing it right C# regarding parsing using delegate?

I want to parse string into version numbers using delegate. The delegate requires a string as an argument and produces integer array as an output.
There are two errors
Error CS0136 A local or parameter named 'arrayString' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter
Error CS0029 Cannot implicitly convert type 'System.Collections.Generic.List<int>' to 'int'
namespace TEST
{
class Program
{
public delegate int Parsing(string parsee);
static void Main(string[] args)
{
Parsing parsee = new Parsing(Parse);
Console.WriteLine();
}
public static int Parse(string arrayString)
{
Console.WriteLine("Write the version numbers : ");
var input = Console.ReadLine();
string[] arrayString = input.Split('-');
List<int> listInt = new List<int>();
foreach (string i in arrayString)
{
listInt.Add(Convert.ToInt32(i));
}
listInt.ToArray();
foreach (var item in listInt)
{
Console.WriteLine(item);
}
return listInt;
}
}
}
I am a noob.
The identifier "arrayString" is defined twice.
Local variables as well as arguments are not allowed to be defined with the same name, otherwise accessing the identifier would have ambiguous meaning.
As long as the Parse method is named "parse", it should act as-is. It takes an argument as input and outputs the produced result, which is the only duty of it. while Console.ReadLine or Console.WriteLine are not a part of "Parse" but "Input".
You need more learning on the basic syntax and the built-in types before you start learning delegate.
The solution that maybe matches your requirement as a demo is:
namespace Test
{
internal static class Program
{
internal static void Main(string[] args)
{
Console.WriteLine("Write the version numbers : ");
var input = Console.ReadLine();
var results = ParseInts(input.Split('-'), ParseInt);
foreach (var item in results)
{
Console.WriteLine(item);
}
}
private static IEnumerable<int> ParseInts(IEnumerable<string> values, Func<string, int> parser)
{
foreach (var item in values)
{
yield return parser(item);
}
}
private static int ParseInt(string value)
=> Convert.ToInt32(value);
}
}
But simply we use this to get int values:
var input = Console.ReadLine();
var results = input.Split('-').Select(int.Parse);
instead of writing everything.

Assert.That() does not evaluate given constraint

I have a list of string values. I iterate this list with a foreach. I apply on every list item Assert.That() method. Now comes the twist: I give as a second parameter of the method a function that returns IResolveConstraint. When the function returns Has.Exactly(1).Contains(), then AT THE SECOND item of the list (second iteration of the foreach) the assertion evaluates (throws) this message: "Expected: collection containing "value"". But the assertion was supposed to pass, because it was verifying exactly one item, not a collection.
I had NUnit 3.2.1. I upgraded to version 3.12.0 and after that the message changed from : "Expected: collection containing "value"" to message: "Expected: some item equal to "value"" and assertion still didn' pass.
public class Verifiers
{
public void VerifyCollectedValues(List<string> collectedValues, List<string> expectedValues
, string collectedValuesFrom, int countOfValueExpactance, bool verifyExactSameValue = true)
{
// Create a constraint function
Func<string, IResolveConstraint> constraintFunction = CreateConstraintFunction(Has.Exactly(countOfValueExpactance), verifyExactSameValue);
// Pass the constraint to the method
VerifyCollectedValues(constraintFunction, collectedValues, expectedValues, collectedValuesFrom);
}
public void VerifyCollectedValues(Func<string, IResolveConstraint> constraintFunction, List<string> collectedValues, List<string> expectedValues
, string collectedValuesFrom)
{
foreach (string expectedValue in expectedValues)
{
// Apply the constraint
Assert.That(collectedValues, constraintFunction(expectedValue));
}
}
public Func<string, IResolveConstraint> CreateConstraintFunction(ConstraintExpression constraintExpression, bool verifyExactSameValue)
{
if (verifyExactSameValue)
{
return (string value) => constraintExpression.EqualTo(value);
}
else
{
return (string value) => constraintExpression.Contains(value);
}
}
}
Sample code:
Verifiers verifiers = new Verifiers();
List<string> expectedValues = new List<string>()
{
"value1",
"value2",
"value3",
};
var collectedValues = new List<string>()
{
"some_value0",
"some_value1",
"some_value2",
"some_value3",
};
// This passes
foreach(string expectedValue in expectedValues)
{
Assert.That(collectedValues, Has.Exactly(1).Contains(expectedValue));
}
// This fails with the message: "Expected: collection containing "value2"" (with NUnit v3.2.1) / "Expected: some item equal to "value2""(with NUnit v3.12.0)
verifiers.VerifyCollectedValues(collectedValues, expectedValues, 1, false);
My assumption is that IResolveConstraint is causing the problem. What I don't understand is why the first item of list passes but the second one doesn't.
I'll be grateful for any answer.
UPDATE:
I've done some edits and base on that I excluded the possibility that IResolveConstraint is causing the problem. Here is sample of the current code:
public class Verifiers
{
public void VerifyCollectedValues(List<string> collectedValues, List<string> expectedValues, string collectedValuesFrom, int countOfValueExpectance, bool verifyExactSameValue = true)
{
VerifyCollectedValues(CreateConstraintExpressionExactly(countOfValueExpectance), collectedValues, expectedValues, collectedValuesFrom, verifyExactSameValue);
}
private void VerifyCollectedValues(ConstraintExpression constraintExpression, List<string> collectedValues, List<string> expectedValues, string collectedValuesFrom, bool exactSameValue)
{
if (exactSameValue)
{
VerifyCollectedValuesEqualTo(constraintExpression, collectedValues, expectedValues, collectedValuesFrom);
}
else
{
VerifyCollectedValuesContains(constraintExpression, collectedValues, expectedValues, collectedValuesFrom);
}
}
private void VerifyCollectedValuesEqualTo(ConstraintExpression constraintExpression, List<string> collectedValues, List<string> expectedValues, string collectedValuesFrom)
{
Func<string, EqualConstraint> constraintFunction = (string value) => constraintExpression.EqualTo(value);
foreach (string expectedValue in expectedValues)
{
Assert.That(collectedValues, constraintFunction(expectedValue));
}
}
private void VerifyCollectedValuesContains(ConstraintExpression constraintExpression, List<string> collectedValues, List<string> expectedValues, string collectedValuesFrom)
{
// if I don't use constraintExpression but write the expression manually (Has.Exactly(1).Contains(value)), then it works just fine
Func<string, ContainsConstraint> constraintFunction = (string value) => constraintExpression.Contains(value);
foreach (string expectedValue in expectedValues)
{
Assert.That(collectedValues, constraintFunction(expectedValue));
}
}
private ConstraintExpression CreateConstraintExpressionExactly(int countOfExpectance)
{
return Has.Exactly(countOfExpectance);
}
var verifiers = new Verifiers();
List<string> expectedValues = new List<string>()
{
"value1",
"value2",
"value3",
};
var collectedValues = new List<string>()
{
"some_value0",
"some_value1",
"some_value2",
"some_value3",
};
// This one pass successfully
foreach(string expectedValue in expectedValues)
{
Assert.That(collectedValues, Has.Exactly(1).Contains(expectedValue));
}
// But this one still doesn't pass and fails at the second list item "value2"
verifiers.VerifyCollectedValues(collectedValues, expectedValues, "list of strings", 1, false);
Your assumption that Has.Exactly(1).EqualTo() will pass if the actual value is a non-collection is not correct. ExactCountConstraint will error (not quite the same as fail) if passed a non-collection type.
However, in this case, string is a collection of chars so it tries to run.
The problem is that you are using a collection constraint, which iterates the list, but you are also iterating it yourself. You should either do it all yourself (which seems silly) or rely on the ExactCountConstraint to do the work.
If you continue to iterate the list yourself, then you should not use any collection constraints. In the example case, you would simply use Is.EqualTo().
Well, the bug is in your implementation actually, since you're passing ConstraintExpression all the time.
If you try to decompose the working code and create methods for each verification you'll have something like this:
[Test]
public void ListCompareTest()
{
List<string> expectedValues = new List<string>()
{
"value1",
"value2",
"value3",
};
var collectedValues = new List<string>()
{
"some_value0",
"some_value1",
"some_value2",
"some_value3",
};
int count = 1;
// This one pass successfully
foreach (string expectedValue in expectedValues)
{
ItemsConstraintExpression itemsConstraintExpression = GetExactly(count);
// this code works
Assert.That(collectedValues, GetContains(expectedValue, itemsConstraintExpression));
}
// this code works as well
DoVerification(expectedValues, collectedValues, 1, false);
}
public static void DoVerification(List<string> expectedList, List<string> actualList, int exactlyCount, bool equalsOrContains)
{
if (equalsOrContains)
{
foreach (var expectedValue in expectedList)
{
Assert.That(actualList, GetEquals(expectedValue, GetExactly(exactlyCount)));
}
}
else
{
foreach (var expectedValue in expectedList)
{
Assert.That(actualList, GetContains(expectedValue, GetExactly(exactlyCount)));
}
}
}
private static EqualConstraint GetEquals(string expectedValue, ItemsConstraintExpression itemsConstraintExpression)
{
return itemsConstraintExpression.EqualTo(expectedValue);
}
private static ContainsConstraint GetContains(string expectedValue, ItemsConstraintExpression itemsConstraintExpression)
{
return itemsConstraintExpression.Contains(expectedValue);
}
private static ItemsConstraintExpression GetExactly(int count)
{
return Has.Exactly(count);
}

Display all strings in a List

I am trying to create two sets and put both in a list and display all items in the list. I am getting an error with my code.
Error: System.Collections.Generic.List1[System.Collections.Generic.SortedSet1[System.String]]
I am attaching my code below. Any help is appreciated.
namespace Prog 5
{
class Program
{
static void Main(string[] args)
{
List<SortedSet<string>> items = new List<SortedSet<string>>();
SortedSet<string> set = new SortedSet<string>();
SortedSet<string> set2 = new SortedSet<string>();
set.Add("a");
set.Add("b");
set.Add("d");
set.Add("c");
set2.Add("e");
set2.Add("d");
set2.Add("c");
foreach (string item in set)
{
items.Add(set);
}
foreach (string item in set2)
{
items.Add(set2);
}
DisplayItem(items);
}
public static void DisplaySet(SortedSet<string> set)
{
string set1 = string.Join(",", set);
Console.WriteLine(set1);
Console.ReadLine();
}
public static void DisplayItem(List<SortedSet<string>> items)
{
foreach (SortedSet<string> item in items)
{
Console.WriteLine(items);
Console.ReadLine();
}
}
}
In DisplayItem(...) you have Console.WriteLine(items)... which is type List>. ToString() automatically gets called on that to produce a string for Console.WriteLine to output: that's why you get your current message. I'm guessing you want to write each item to console.
public static void DisplayItem(List<SortedSet<string>> items)
{
foreach (SortedSet<string> item in items)
{
DisplaySet(item);
}
}
SortedSet<string> inherits from Object and the default behavior of ToString() is GetType().ToString();, because of that you receive System.Collections.Generic.List1[System.Collections.Generic.SortedSet1[System.String]] in the console. Reference Code
public virtual String ToString()
{
return GetType().ToString();
}
SOLUTION: If you want to show elements of the SortedSet in the console you should use this:
Console.WriteLine(string.Join(",", item.ToArray()));
This will concatenate all the strings in the SortedSet and show them in the console with , separator.
If I understood your requirements correctly, you are looking to merge 2 SortedSets into single list or perhaps set.
The following code should work (explanations of why it is working follow):
class Program
{
static void Main(string[] args)
{
SortedSet<string> items = new SortedSet<string>();
SortedSet<string> set = new SortedSet<string>();
SortedSet<string> set2 = new SortedSet<string>();
set.Add("a");
set.Add("b");
set.Add("d");
set.Add("c");
set2.Add("e");
set2.Add("d");
set2.Add("c");
foreach (string item in set)
{
items.Add(item);
}
foreach (string item in set2)
{
items.Add(item);
}
DisplayItem(items);
}
public static void DisplaySet(SortedSet<string> set)
{
string set1 = string.Join(",", set);
Console.WriteLine(set1);
Console.ReadLine();
}
public static void DisplayItem(SortedSet<string> items)
{
foreach (string item in items)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
}
The main issue in code listed in question is that it is attempting to create a List of OrderedSet<string> objects. Since each OrderedSet<string> represents a collection of strings you need to iterate over these collections and put strings into a new collection. The code presented in question does not do it. Instead of that it is adding 7 OrderedSet<string> objects into the List.
Corrected code fixes that issue and adds strings to a new OrderedList<string>. You can decide what collection it is depending on your requirements. If you need collection not to contain duplicates and be sorted you can choose a new OrderedSet<string>, however if you don't care about duplicates then you can choose a
List<string>.
If you wish to compare the difference between OrderedSet<string> and List<string> you can simply change 'items' data type to List<string> and run the program.
Current output:
a
b
c
d
e
If you change
SortedSet<string> items = new SortedSet<string>();
to
List<string> items = new List<string>();
...
public static void DisplayItem(List<string> items)
{
...
you will get:
a
b
c
d
c
d
e

Issue with extension method

Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
namespace LambdaExtensionEx
{
class Program
{
static void Main(string[] args)
{
string[] myStrngs = new string[] { "Super","Simple","Okay"};
IEnumerable<string> criteraStrngs = myStrngs.WhereSearch<string>(delegate(string s)
{
return s.StartsWith("s");
});
string s1 = "dotnet";
int count = s1.GetCount();
Console.ReadLine();
}
}
public static class MyExtension
{
public delegate bool Criteria<T>(T Value);
public static IEnumerable<T> WhereSearch<T>(this IEnumerable<T> values, Criteria<T> critera)
{
foreach (T value in values)
if (critera(value))
yield return value;
}
public static int GetCount(this string value)
{
return value.Count();
}
}
}
I am able to call GetCount extension method and I get result in 'count'. But WhereSearch is not being called in any time and not getting result. What mistake am I doing?
You need to start enumerating over the result returned by the WhereSearch function if you want it to get executed. The reason for that is because this function yield returns an IEnumerable<T>. What the C# compiler does is build a state machine and doesn't execute the function immediately until the calling code starts enumerating over the result.
For example:
// The function is not executed at this stage because this function uses
// yield return to build an IEnumerable<T>
IEnumerable<string> criteraStrngs = myStrngs.WhereSearch<string>(delegate(string s)
{
return s.StartsWith("s");
});
// Here we start enumerating over the result => the code will start executing
// the function.
foreach (var item in criteraStrngs)
{
Console.WriteLine(item);
}
Another example is calling some of the LINQ extension methods such as .ToList() on the result which will actually enumerate and call the function:
IEnumerable<string> criteraStrngs = myStrngs.WhereSearch<string>(delegate(string s)
{
return s.StartsWith("s");
})
.ToList();
For more details on how lazy loading works in this case you may take a look at the following post.
Your extension methods class doesn't make much sense - why don't you just do this?
class Program
{
static void Main(string[] args)
{
string[] myStrngs = new string[] { "Super", "Simple", "Okay" };
IEnumerable<string> criteraStrngs = myStrngs.Where(x => x.StartsWith("s"));
string s1 = "dotnet";
int count = s1.Count();
Console.ReadLine();
}
}
As stated by Darin Dimitrov previously - you will still need to enumerate criteriaStrngs to get a result out of it - which is what was wrong with your original code.
HTH

Categories