Pass multiple parameters to a task - c#

I wish to pass two BlockingCollection<>s to a task. I tried to put them in an object array and pass them but it doesn't work. Can anyone help me with this? The code where i am trying to pass the values is written below:
var lineHolders = new[]
{
new BlockingCollection<string>(linesCapacity),
new BlockingCollection<string>(linesCapacity),
new BlockingCollection<string>(linesCapacity),
new BlockingCollection<string>(linesCapacity)
};
var chunksHolder = new[]
{
new BlockingCollection<List<BsonDocument>>(chunksCapacity),
new BlockingCollection<List<BsonDocument>>(chunksCapacity)
};
for (var processors = 0; processors < 16; processors++)
{
var myLineHolder = lineHolders[processors%lineHolders.Length];
var myChunkHolder = chunksHolder[processors%chunksHolder.Length];
processorTaskArray[processors] = Task.Factory.StartNew((arg) =>
{
var lines = (BlockingCollection<string>) arg[0]; // compiler generates error here
var chunks = (BlockingCollection<List<BsonDocument>>) arg[1]; // compiler generates error here
// perform my work...
},
new object []
{
myLineHolder,
myChunkHolder
});
}

You're using the following overload of StartNew:
public Task StartNew(
Action<Object> action,
Object state
)
Since it's just an object you can't apply indexing on it. Cast it and it will work fine.
for (var processors = 0; processors < 16; processors++)
{
var myLineHolder = lineHolders[processors % lineHolders.Length];
var myChunkHolder = chunksHolder[processors % chunksHolder.Length];
processorTaskArray[processors] = Task.Factory.StartNew((arg) =>
{
var properArg = (object[]) arg;
var lines = (BlockingCollection<string>) properArg[0]; // compiler generates error here
var chunks = (BlockingCollection<List<BsonDocument>>) properArg[1]; // compiler generates error here
// perform my work...
},
new object[]
{
myLineHolder,
myChunkHolder
});
}

Related

How to setup the same method multiple times with different arguments?

I have an object under test with a method called void Print(string msg).
The object invoke the method multiple times and passes a different message.
For example...
Strait forward of the usage:
public interface IMyPrinter
{
void Print(string msg);
}
public class Printer : IMyPrinter
{
public void Print(string msg)
{
// print to console
}
}
public class MyObject
{
public IMyPrinter Printer { get; set; }
public void foo()
{
for (var count = 0; count < 4; count++)
{
Printer.Print($"abc_{count}");
}
}
}
When I want to test foo method, how can setup the Mock object to capture the different Print methods calls?
I tried:
var printerMock = new Mock<IMyPrinter>(MockBehavior.Loose);
for (var count = 0; count < 4; count++)
{
printerMock.Setup(_ => _.Print($"abc_{count}");
}
var underTest = new MyObject();
underTest.Printer = printerMock.Object;
underTest.foo();
printerMock.VerifyAll();
but this of course made only the last setup effective (when count = 3).
How can this be done?
Another option, since in this case the mocked member has no expected behavior in a loose mock, would be to use the loop for verification instead of setup.
For example
// Arrange
var printerMock = new Mock<IMyPrinter>(MockBehavior.Loose);
var underTest = new MyObject();
underTest.Printer = printerMock.Object;
// Act
underTest.foo();
// Assert
for (int count = 0; count < 4; count++) {
string msg = $"abc_{count}";
printerMock.Verify(_ => _.Print(msg), Times.Once);
}
If you want to capture the actual arguments passed, then a call back can be used
// Arrange
List<string> expected = new List<string>() { ... }; //populated
List<string> actual = new List<string>();
var printerMock = new Mock<IMyPrinter>(MockBehavior.Loose);
printerMock
.Setup(_ => _.Print(It.IsAny<string>()))
.Callback(string msg => actual.Add(msg));
var underTest = new MyObject();
underTest.Printer = printerMock.Object;
// Act
underTest.foo();
// Assert
//..If messages are known before hand then they can be used to assert the
//the captured messages from when the mock was invoked.
actual.Should().BeEquivalentTo(expected); //Using FluentAssertions
but this of course made only the last setup effective (when count = 3).
actually that is not true, if you look closely to the test output you will see that it contains 4 calls with the value of 4!:
IMyPrinter _ => _.Print("abc_4")
IMyPrinter _ => _.Print("abc_4")
IMyPrinter _ => _.Print("abc_4")
IMyPrinter _ => _.Print("abc_4")
this is the value that count has after the loop has ended. Since you are using a lambda expression, your count variable is caught in closure and the last value 4 was captured. Only this value is used when the lambda expression is evaluated later on in your code when the loop has finished a long time ago. You need to create a temporaty variable and capture the index in it. And your test will run green:
var printerMock = new Mock<IMyPrinter>(MockBehavior.Loose);
for (var count = 0; count < 4; count++)
{
int i = count; // <-- this is the main change
printerMock.Setup(_ => _.Print($"abc_{i}")); // <-- use "i" in lambda
}
var underTest = new MyObject();
underTest.Printer = printerMock.Object;
underTest.foo();
printerMock.VerifyAll();
EDIT:
for further reading on closures I recommend this article by Jon Skeet

How to add forloop in If-Else condition in c#?[CODEDOM]

How to execute the following code in Codedom:
if(statement)
{
for(int = 0; i < 10; i++)
{
Console.WriteLine(i);
}
}
I'm familiar with CodeIterationStatement and CodeConditionStatement but don't know how to execute it.
What type of statement or condition you want to put to execute inside loop is depend upon requirement. I guess there should be a condition inside if statement lets understand by example
int a=5;
if(a>1)
{
for(int i=0;i<10;i++)
{
Console.WriteLine("{0}",i);
}
}
This should do the trick
private CodeConditionStatement makeIfStatementWithLoop(CodeExpression conditionExpr, CodeExpression iterationLimit)
{
CodeConditionStatement ifStatement = new CodeConditionStatement();
ifStatement.Condition = conditionExpr;
CodeVariableDeclarationStatement iDeclaration = new CodeVariableDeclarationStatement(new CodeTypeReference(typeof(int)), "i");
CodeVariableReferenceExpression iReference = new CodeVariableReferenceExpression(iDeclaration.Name);
CodeIterationStatement loopStatement = new CodeIterationStatement();
loopStatement.InitStatement = new CodeAssignStatement(iReference, new CodePrimitiveExpression(0));
CodeAssignStatement incrementStatement = new CodeAssignStatement();
incrementStatement.Left = iReference;
incrementStatement.Right = new CodeBinaryOperatorExpression(iReference, CodeBinaryOperatorType.Add, new CodePrimitiveExpression(1));
loopStatement.IncrementStatement = incrementStatement;
loopStatement.TestExpression = new CodeBinaryOperatorExpression(iReference, CodeBinaryOperatorType.LessThan, iterationLimit);
CodeVariableReferenceExpression consoleRef = new CodeVariableReferenceExpression("Console");
CodeExpression[] args = new CodeExpression[] { iReference };
CodeMethodInvokeExpression consoleWriteLineStmt = new CodeMethodInvokeExpression(consoleRef, "WriteLine", args);
loopStatement.Statements.Add(consoleWriteLineStmt);
ifStatement.TrueStatements.Add(loopStatement);
return ifStatement;
}

Values don't leave the stack after Pop (Stack<T>)

The stack is formed from a list that was grouped. Then there are several foreachs where Pop is called.
But the number of values in the stacks does not decrease.
Please explain why. Thank you in advance!
public static void N(){
var test = new List<int>(){1,1,1,2,2,3,3,3,3,4,4,5,6,7,7,7,7,8};
var testGr = test.GroupBy(x=>x);
var stacked = testGr.Select(x=> new {
num = x.Key,
stack = new Stack<int>(x)
});
var testNumOne = stacked.First(x=>x.num==1);
Console.WriteLine(testNumOne.stack.Count());
foreach(var x in new List<int>(){1,2,1,2,3}){
var flush = stacked.First(y => y.num == x).stack.Pop();
}
testNumOne = stacked.First(x=>x.num==1);
Console.WriteLine(testNumOne.stack.Count());
}
The copy of "stacked" solves the problem (through stacked.ToList())
public static void Main()
{
var test = new List<int>(){1,1,1,2,2,3,3,3,3,4,4,5,6,7,7,7,7,8};
var testGr = test.GroupBy(x=>x);
var stacked = testGr.Select(x=> new {
num = x.Key,
stack = new Stack<int>(x)
});
var stackedCopy = stacked.ToList();
var testNumOne = stackedCopy.First(x=>x.num==1);
Console.WriteLine(testNumOne.stack.Count());
foreach(var x in new List<int>(){1,2,1,2,3}){
var flush = stackedCopy.First(y => y.num == x).stack.Pop();
}
testNumOne = stackedCopy.First(x=>x.num==1);
Console.WriteLine(testNumOne.stack.Count());
}
I'm already learning how the Linq library works with memory. Thank you all very much!)

Got NullReferenceException When I use same code but different expression

I'm writing a project about game's character data.
And each character in the data document have four types, Lv1 and LvMAX, and HP, STR, VIT, INT, MEN.
I use top one code at the middle part and got NullReferenceException when I use it to get some data like:
int x = CD.Parameters.Basic.Awaked.Force.Lv1.STR;
Force will be null. But when I use buttom one at the middle part, Force won't be null.
What's the difference between that two?
Code below
public class ParamType
{
public ParamLv Mebius, Force, Aegis, Magius;
string cost;
DataRow[] Datas;
List<int> ToMebius = new List<int>(), ToForce = new List<int>(), ToAegis = new List<int>(), ToMagius = new List<int>(); //HP, HP, STR, STR, VIT, VIT, INT, INT, MEN, MEN
public ParamType(SData Data, bool awaked)
{
if (awaked)
{
Data.CharaID = CharaCOM.AwakedID(Data.CharaID);
}
Datas = DataCOM.Search(Data.CharaID, Data.DTs.Source, Data.TitleP.Start[(int)DataTitle.CharacterParams], Const.COL_CHARACTER_ID, Const.COL_CHARACTER_ID);
cost = DataCOM.Search(Data.DTs.Source, Data.CharaID, Const.COL_COST, 0, Data.TitleP.Start[(int)DataTitle.CharacterParams], Const.COL_CHARACTER_ID_WITH_TYPE);
List<int>[] SArray = { ToMebius, ToForce, ToAegis, ToMagius };
for (int i = 0; i < Datas.Length; i++)
{
SArray[i] = new List<int>();
for (int j = Const.COL_PARAM_MIN; j < Const.COL_PARAM_MIN + Const.COL_PARAM_LENGTH; j++)
{
SArray[i].Add(Convert.ToInt32(Datas[i][j]));
}
}
/*
this will send NullReference Exception
ParamLv[] PLArray = new ParamLv[4];
for (int i = 0; i < SArray.Length; i++)
{
PLArray[i] = new ParamLv(Data, SArray[i]);
}
*/
/*
This won't get exception and I can get correct data I want.
Mebius = new ParamLv(Data, SArray[0]);
Force = new ParamLv(Data, SArray[1]);
Aegis = new ParamLv(Data, SArray[2]);
Magius = new ParamLv(Data, SArray[3]);
*/
}
public class ParamLv
{
public Params Lv1, LvMax;
List<int> ToLv1 = new List<int>(), ToLvMAX = new List<int>(); //HP, STR, VIT, INT, MEN
public ParamLv(SData Data, List<int> ParamsL)
{
for (int i = 0; i < ParamsL.Count; i += Const.COL_PARAM_MIN_MAX_GAP)
{
ToLv1.Add(ParamsL[i]);
ToLvMAX.Add(ParamsL[i + 1]);
}
Lv1 = new Params(Data, ToLv1);
LvMax = new Params(Data, ToLvMAX);
}
public class Params
{
//some method and properties to get or set Parameters.
}
}
Please tell me if something still bad, and this is my first time to ask question here, so If I did something wrong, please tell me. Thanks for #MicroVirus , #Moriarty and #mvikhona told my mistake.
Mebius = new ParamLv(Data, SArray[0]);
Force = new ParamLv(Data, SArray[1]);
Aegis = new ParamLv(Data, SArray[2]);
Magius = new ParamLv(Data, SArray[3]);
This works, because you are assigning reference to new ParamLv to your properties.
But in this case:
ParamLv[] PLArray = { Mebius, Force, Aegis, Magius };
for (int i = 0; i < PLArray.Length; i++)
{
PLArray[i] = new ParamLv(Data, SArray[i]);
}
you aren't filling your array with variables/properties themselves, but you are filling it with references your properties hold, in the end your array will hold reference to 4 new ParamLw, but your property Force will stay null.
Edit:
I'll try to explain it a bit different. Let's say you have this code:
ParamLv[] PLArray = { Force };
At this moment value of PLArray[0] is same as value of Force, but PLArray[0] isn't Force.
The moment you do this:
PLArray[0] = new ParamLv(Data, null);
new ParamLv(Data, null) returns reference to new ParamLv and you assign this to your PLArray[0], but like I said before PLArray[0] isn't Force, so Force will stay unchanged.
If that didn't explain it well, try to look at this piece of code, it does what you are trying to do.
int a = 1;
int[] myArr = { a }; // a = 1, myArr[0] = 1
myArr[0] = 2; // a = 1, myArr[0] = 2
object obj = null;
object[] objArr = { obj }; // obj = null, objArr[0] = null
objArr[0] = new object(); // obj = null, objArr[0] = 'reference to new object'

How to pass a Method with parameters to another method

I'm trying to build up a Data Matrix which comprises a list of objects. So here's what I am trying to do:
List<IBasePremium> BasePremiumMatrix = new List<IBasePremium>();
List<ICalcRate> calcRates = new List<ICalcRate>
{
new CalcRate { BasePremiumType = 1, Rate = basePremiumRate.Building, Calc = basePremiumRate.Building },
new CalcRate { BasePremiumType = 2, Rate = basePremiumProduct.Building,Calc = calculator.BasePremium(basePremiumProduct.Building,basePremiumRate.Building) }
// new CalcRate { BasePremiumType = 3, Rate = (decimal)postcodeMultiplier.BuildingsCore ,Calc = calculator.BasePremium(postcodeMultiplier.BuildingsCore, ) },
};
on my line of code that is commented out, as the second parameter I really want to pass the value of 'Calc' from the previous line of code. I've got a number of lines like this where I need to pass the previous 'Calc' value to build the matrix. The above is clearly the wrong approach and thought that I'd be able to write a method that takes the form something like :
public CalcRate Multiplier(Func<string,decimal>, int basePremiumType, decimal rate) {.....}
But I'm fighting witrh passing the method name and it's parameter values.
Create and Action or a Func :
Action customAction = ()=> yourFunctionName(param1, param2);
then pass it to the multiplier.
var calcul = Multiplier(customAction , ....);
How about a for loop?
List<IBasePremium> BasePremiumMatrix = new List<IBasePremium>();
List<ICalcRate> calcRates = new List<ICalcRate>();
for (int i = 1; i < max; i++) {
CalcRate rate = new CalcRate { BasePremiumType = i , Rate = basePremiumRate.Building };
if ( i > 1) {
rate.Calc = calcRates.get(i - 2)
} else {
rate.Calc = calculator.BasePremium(basePremiumProduct.Building,basePremiumRate.Building);
}
calcRates.add(rate);
}

Categories