Issue with extension method - c#

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

Related

How to get parameter name with 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));

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.

How to invoke a list of Task<T> by using Parallel

I've got a list of async calls that are lined up in specific order and it does not matter which one finishes first or last. All of these async Task returns Bitmaps. All of the async Task return a single Bitmap accept for one and it returns a list of Bitmaps List.
For testing purposes and me being able to get a better handle on the difference of using Parallel vs just Task I need someone to show me how to invoke each one of these async Task and set a local variable that contains a list of all the returned async results.
How to Parallel.ForEach of these task
How to retrieve the value of each completed task and set a local variable with the returned result.
---Code where I just await each Task one after another.
public async static Task<PdfSharp.Pdf.PdfDocument> RollUpDrawingsPDF(IElevation elevation)
{
List<Bitmap> allSheets = new List<Bitmap>();
var processedParts = new PartsProcessor.PartProcessor().ProcessParts(elevation);
//elevation
allSheets.Add(await ShopDrawing.Manager.GetElevationDrawing(elevation, true, RotateFlipType.Rotate90FlipNone));
//door schedules, 3 schedules per sheet
allSheets.AddRange(await ShopDrawing.Door.GetDoorSecheduleSheets(elevation, RotateFlipType.Rotate90FlipNone, 3));
//materials list
allSheets.Add(await MaterialsList.Manager.GetMaterialList(processedParts).GetDrawing());
//optimized parts
allSheets.Add(await Optimization.Manager.GetOptimizedParts(processedParts).GetDrawing());
//cut sheet
allSheets.Add(await CutSheet.Manager.GetCutSheet(processedParts).GetDrawing());
return await PDFMaker.PDFManager.GetPDF(allSheets, true);
}
------Code I'm tring to run in Parallel.ForEach however this isn't working but a starting place for help. For each returned task result I need to set the local variable of allSheets of that Parallel Task Result.
public async static Task<PdfSharp.Pdf.PdfDocument> RollUpDrawingsPDF(IElevation elevation)
{
List<Bitmap> allSheets = new List<Bitmap>();
var processedParts = new PartsProcessor.PartProcessor().ProcessParts(elevation);
Task[] myTask = new Task[5];
myTask[0] = ShopDrawing.Manager.GetElevationDrawing(elevation, true, RotateFlipType.Rotate90FlipNone);
myTask[1] = ShopDrawing.Door.GetDoorSecheduleSheets(elevation, RotateFlipType.Rotate90FlipNone, 3);
myTask[2] = MaterialsList.Manager.GetMaterialList(processedParts).GetDrawing();
myTask[3] = Optimization.Manager.GetOptimizedParts(processedParts).GetDrawing();
myTask[4] = CutSheet.Manager.GetCutSheet(processedParts).GetDrawing();
var x = Parallel.ForEach(myTask, t => t.Wait());
////elevation
//allSheets.Add(await );
////door schedules, 3 schedules per sheet
//allSheets.AddRange(await);
////materials list
//allSheets.Add(await );
////optimized parts
//allSheets.Add(await );
////cut sheet
//allSheets.Add(await );
return await PDFMaker.PDFManager.GetPDF(allSheets, true);
}
How would I implement the Parallel.ForEach for this body of code?
*Discussion code example. How to return a List when other methods return one Bitmap*
async Task<Bitmap[]> RollUpHelper(IElevation elevation, PartsProcessor.ProcessedParts processedParts)
{
return await Task<Bitmap[]>.WhenAll(
ShopDrawing.Manager.GetElevationDrawing(elevation, true, RotateFlipType.Rotate90FlipNone),
//ShopDrawing.Door.GetDoorSecheduleSheets(elevation,RotateFlipType.Rotate90FlipNone, 3),
MaterialsList.Manager.GetMaterialList(processedParts).GetDrawing(),
MaterialsList.Manager.GetMaterialList(processedParts).GetDrawing(),
CutSheet.Manager.GetCutSheet(processedParts).GetDrawing()
);
}
Parallel.ForEach() is for running multiple synchronous operations in parallel.
You want to wait for a number of asynchronous Tasks to finish:
await Task.WhenAll(tasks);
To expand upon SLaks' answer:
Task.WhenAll() will return an array of all the results returned by the tasks it waited for, so you don't need to manage that yourself.
Here's an example where I use string instead of Bitmap as in your example. Note how one of the workers doesn't return a List<string> and I convert it to a List<string> with one item, to make it the same type as the others.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
class Data
{
public string Value;
public Data(string value) { Value = value; }
}
class Program
{
async Task<List<string>[]> RunAsync()
{
return await Task.WhenAll
(
Task.Factory.StartNew(() =>
new List<string> {Worker1(new Data("One"))}),
Task.Factory.StartNew(() =>
Worker2(new Data("Two"))),
Task.Factory.StartNew(() =>
Worker3(new Data("Three")))
);
}
void Run()
{
var results = RunAsync().Result;
// Now results is an array of List<string>, so we can iterate the results.
foreach (var result in results)
{
result.Print();
Console.WriteLine("--------------");
}
}
string Worker1(Data data)
{
Thread.Sleep(1000);
return data.Value;
}
List<string> Worker2(Data data)
{
Thread.Sleep(1500);
return Enumerable.Repeat(data.Value, 2).ToList();
}
List<string> Worker3(Data data)
{
Thread.Sleep(2000);
return Enumerable.Repeat(data.Value, 3).ToList();
}
static void Main()
{
new Program().Run();
}
}
static class DemoUtil
{
public static void Print(this object self)
{
Console.WriteLine(self);
}
public static void Print(this string self)
{
Console.WriteLine(self);
}
public static void Print<T>(this IEnumerable<T> self)
{
foreach (var item in self)
Console.WriteLine(item);
}
}
}

I want to sort the string array. I know to achieve the result in diff ways. But I want to know why this method is throwing an error

I'm getting an
Object reference not set
exception in this Program..
Where I store Output1[k++] there is a problem...
CODE:
Class stringsor
{
public static string[] output1;
public static void sortstrings(string[] input1)
{
int k = 0;
foreach (var item in input1)
{
output1[k++] = (item.OrderBy(i => i)).ToString();
}
Sorting Using Linq
output1 = new string[k];
foreach(var item in output1)
{
Console.WriteLine(item);
}
}
public static void Main(string[] args)
{
string[] input1 = { "Adkad","jor","ioeuo","zkas","aka","nma"};
sortstrings(input1);
}
}
You have declared output1, but not initialized it.
Before you use it in sortStrings, try.
output1 = new string[input1.Length];
problem is you are not initializing output array before using it. Since you already using LINQ you can initialize and assign output array directly like below
public static void sortstrings(string[] input1)
{
output1 = input1.Select(word => new string(word.OrderBy(i => i).ToArray())).ToArray();
foreach (var item in output1)
{
Console.WriteLine(item);
}
}
I want to sort the string array.
But what you currently doing is reversing the order of characters in the output array. it is not sort the string array. is that what you expect? if you need to order the strings you can so as below
output1 = input1.OrderBy(word => word).ToArray();

Limit RemoveAll to a certain number of objects

I am working with a List<T> which contains both parent and children objects. In this list children objects are aware of their related parent object and vice versa. Using this list I am trying to implement a business rule where up to 4 children objects will be removed from the list when their parent is of a certain type. Put differently if a parent of this type has 20 children 4 of them should be removed from the list.
The code I have outlined here will RemoveAll of the children objects that meet the condition. This is expected but what I'd like to do is limit the RemoveAll to removing only 4 children. Is there a means to do this with RemoveAll or is there another method I should be using?
myList.RemoveaAll(item =>
item.Child && "Foo".Equals(item.Parent.SpecialType));
The Take extension method is used to grab the first n number of matches from an IEnumerable. You can then iterate through the matches and remove them from the list.
var matches = myList.Where(item => item.Child && "Foo".Equals(item.Parent.SpecialType)).Take(someNumber).ToList();
matches.ForEach(m => myList.Remove(m));
Does it matter which 4? If not, you can use .Take(4) to create a list of 4 children, then iterate through and Remove the 4...
try this:
int i = 0;
myList.Removeall(item =>
item.Child && "Foo".Equals(item.Parent.SpecialType) && i++ < 4);
Note that I haven't tested it but it should work
Why not use the Take function?
You could also write an extension method to build on top of the normal list interface like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace App
{
public static class ListExtension
{
public static int RemoveAll<T>(this List<T> list, Predicate<T> match, uint maxcount)
{
uint removed = 0;
Predicate<T> wrappingmatcher = (item) =>
{
if (match(item) && removed < maxcount)
{
removed++;
return true;
}
else
{
return false;
}
};
return list.RemoveAll(wrappingmatcher);
}
}
public interface IHero { }
public class Batman : IHero { }
public class HeroCompilation
{
public List<IHero> Herolist;
public HeroCompilation()
{
Herolist = new List<IHero>();
}
public void AddBatmans(int count){
for (int i = 1; i <= count; i++) Herolist.Add(new Batman());
}
}
class Program
{
static void ConsoleWriteBatmanCount(List<IHero> hero)
{
Console.WriteLine("There are {0} Batmans", hero.Count);
}
static void Main(string[] args)
{
HeroCompilation tester = new HeroCompilation();
ConsoleWriteBatmanCount(tester.Herolist);
tester.AddBatmans(10);
ConsoleWriteBatmanCount(tester.Herolist);
tester.Herolist.RemoveAll((x) => { return true; }, 4);
ConsoleWriteBatmanCount(tester.Herolist);
tester.Herolist.RemoveAll((x) => { return true; }, 4);
ConsoleWriteBatmanCount(tester.Herolist);
tester.Herolist.RemoveAll((x) => { return true; }, 4);
ConsoleWriteBatmanCount(tester.Herolist);
while (true) ;
}
}
}

Categories