In the following program, DummyMethod always print 5. But if we use the commented code instead, we get different values (i.e. 1, 2, 3, 4). Can anybody please explain why this is happenning?
delegate int Methodx(object obj);
static int DummyMethod(int i)
{
Console.WriteLine("In DummyMethod method i = " + i);
return i + 10;
}
static void Main(string[] args)
{
List<Methodx> methods = new List<Methodx>();
for (int i = 0; i < 5; ++i)
{
methods.Add(delegate(object obj) { return DummyMethod(i); });
}
//methods.Add(delegate(object obj) { return DummyMethod(1); });
//methods.Add(delegate(object obj) { return DummyMethod(2); });
//methods.Add(delegate(object obj) { return DummyMethod(3); });
//methods.Add(delegate(object obj) { return DummyMethod(4); });
foreach (var method in methods)
{
int c = method(null);
Console.WriteLine("In main method c = " + c);
}
}
Also if the following code is used, I get the desired result.
for (int i = 0; i < 5; ++i)
{
int j = i;
methods.Add(delegate(object obj) { return DummyMethod(j); });
}
The problem is that you're capturing the same variable i in every delegate - which by the end of the loop just has the value 5.
Instead, you want each delegate to capture a different variable, which means declaring a new variable in the loop:
for (int i = 0; i < 5; ++i)
{
int localCopy = i;
methods.Add(delegate(object obj) { return DummyMethod(localCopy); });
}
This is a pretty common "gotcha" - you can read a bit more about captured variables and closures in my closures article.
This article will probably help you understand what is happening (i.e. what a closure is): http://blogs.msdn.com/oldnewthing/archive/2006/08/02/686456.aspx
If you look at the code generated (using Reflector) you can see the difference:
private static void Method2()
{
List<Methodx> list = new List<Methodx>();
Methodx item = null;
<>c__DisplayClassa classa = new <>c__DisplayClassa();
classa.i = 0;
while (classa.i < 5)
{
if (item == null)
{
item = new Methodx(classa.<Method2>b__8);
}
list.Add(item);
classa.i++;
}
foreach (Methodx methodx2 in list)
{
Console.WriteLine("In main method c = " + methodx2(null));
}
}
When you use the initial code it creates a temporary class in the background, this class holds a reference to the "i" variable, so as per Jon's answer, you only see the final value of this.
private sealed class <>c__DisplayClassa
{
// Fields
public int i;
// Methods
public <>c__DisplayClassa();
public int <Method2>b__8(object obj);
}
I really recommend looking at the code in Reflector to see what's going on, its how I made sense of captured variables. Make sure you set the Optimization of the code to ".NET 1.0" in the Option menu, otherwise it'll hide all the behind scenes stuff.
I think it is because the variable i is put to the heap (it's a captured variable)
Take a look at this answer.
Related
I see that there is a similar question for C++. Does anyone know why this method works when the method is non-generic, but as soon as I make it generic, the random number portion of code fails?
Error: Cannot implicitly convert type int to 'T'. If I can't use generics, I will have to rewrite the same function over and over for each different length of array.
public void fillGenericArray<T>(T[] inputArray) where T : IComparable
{
var randomNumb1 = new Random();
for (int i = 0; i < inputArray.Length - 1; i++)
{
Console.WriteLine($"{inputArray[i] = randomNumb1.Next(1, 501)},");
}
}
I had to look twice at this, but here's the issue:
Because inputArray is an 'array of type T'
then even though i is an int the expression
inputArray[i]
returns a type T not a type int.
And so, conversely, a type T must be assigned to it.
A generic method like this might achieve your goal:
public static void fillGenericArray<T>(T[] inputArray)
{
for (int i = 0; i < inputArray.Length; i++)
{
// Where T has a CTor that takes an int as an argument
inputArray[i] = (T)Activator.CreateInstance(typeof(T), Random.Next(1, 501));
}
}
(Thanks to this SO post for refreshing my memory about instantiating T with arguments.)
You could also use Enumerable.Range() to get the same result without writing a method at all:
// Generically, for any 'SomeClass' with a CTor(int value)
SomeClass[] arrayOfT =
Enumerable.Range(1, LENGTH).Select(i => new SomeClass(Random.Next(1, 501)))
.ToArray();
(Slightly Modified with help from this SO post) - see the answer using Enumerable.Range().
Here is a test runner:
class Program
{
static Random Random { get; } = new Random();
const int LENGTH = 10;
static void Main(string[] args)
{
Console.WriteLine();
Console.WriteLine("With a generic you could do this...");
SomeClass[] arrayOfT;
arrayOfT = new SomeClass[LENGTH];
fillGenericArray<SomeClass>(arrayOfT);
Console.WriteLine(string.Join(Environment.NewLine, arrayOfT.Select(field=>field.Value)));
Console.WriteLine();
Console.WriteLine("But perhaps it's redundant, because Enumerable is already Generic!");
arrayOfT = Enumerable.Range(1, LENGTH).Select(i => new SomeClass(Random.Next(1, 501))).ToArray();
Console.WriteLine(string.Join(Environment.NewLine, arrayOfT.Select(field => field.Value)));
// Pause
Console.WriteLine(Environment.NewLine + "Any key to exit");
Console.ReadKey();
}
public static void fillGenericArray<T>(T[] inputArray)
{
for (int i = 0; i < inputArray.Length; i++)
{
inputArray[i] = (T)Activator.CreateInstance(typeof(T), Random.Next(1, 501));
}
}
class SomeClass
{
public SomeClass(int value)
{
Value = value;
}
public int Value { get; set; }
}
}
Clone or Download this example from GitHub.
There is no reason to use generics. Just replace T with int and you will have function that does what you want (based on your question and comment below it).
EDIT: From your comment it seems you misunderstand the purpose of generics. The non-generic function WILL work for all lengths of the array.
And to answer why the change to generics fails. You are trying to assign int to generic type T which can be anything and compiler will not allow such a cast.
In my code, I cannot figure out why I keep getting 'Process is terminating due to StackOverflowException.' only on the second output.
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
namespace _2018JuniorQ5
{
class Program
{
//Variable Decleration
public static int pages = 0;
public static string[] bookFormat;
public static List<string> alreadyChecked = new List<string>();
public static List<string> nodesToCheck = new List<string>();
public static int level = 0;
public static List<string> childrenNodes = new List<string>();
public static void Main(string[] args)
{
//Get input
pages = Convert.ToInt32(Console.ReadLine());
bookFormat = new string[pages];
for (int x=0; x<pages; x++)
{
bookFormat[x] = Console.ReadLine();
}
//Display if all pages are reachable
Console.WriteLine(getReachablePages(1));
//Find shortest path
List<string> NodeBegin = new List<string>();
NodeBegin.Add("1");
Console.WriteLine(getShortestPath(NodeBegin));
}
public static string getReachablePages(int pageToCheck)
{
string[] options=(bookFormat[pageToCheck - 1]).Split(' ');
alreadyChecked.Add(Convert.ToString(pageToCheck));
for (int a=1; a<=Convert.ToInt32(options[0]); a++)
{
if (!alreadyChecked.Contains(options[a]))
{
getReachablePages(Convert.ToInt32(options[a]));
}
}
if (alreadyChecked.Count == pages)
{
return "Y";
}
else
{
return "N";
}
alreadyChecked.Clear();
}
public static int getShortestPath(List<string> nodesToCheck)
{
level++;
childrenNodes.Clear();
for (int q = 0; q < nodesToCheck.Count; q++)
{
string[] options = bookFormat[Convert.ToInt32(nodesToCheck[q])-1].Split(' ');
if (options[0] == "0")
{
return level;
}
else
{
for (int t = 1; t < options.Length; t++)
{
if (!alreadyChecked.Contains(options[t]))
{
childrenNodes.Add(options[t]);
alreadyChecked.Add(nodesToCheck[q]);
}
}
}
nodesToCheck.Clear();
}
return getShortestPath(childrenNodes);
}
}
}
The first output from the getReachablePages method works, and does not give any errors. However, the second output from the getShortestPath gives the "Process is terminating due to StackOverflowException" error. Can someone explain why the getReachablePages method works, but the getShortestPath method doesn't work?
The problem at the moment is that List<string> is a reference type, so when you pass childrenNodes to getShortestPath (getShortestPath(childrenNodes)), you're actually passing a reference to the same list in memory.
The next thing you do is call childrenNodes.Clear(), which empties that list. Because nodesToCheck and childrenNodes are both pointing at the same list, calling childrenNodes.Clear() means that you have no nodes to check.
Why does this cause a StackOverflowException? It causes one because you have no exit condition for when nodesToCheck is empty. You just keep calling the same method over and over.
I propose the following solution:
public static int getShortestPath(List<string> nodesToCheck)
{
if (!nodesToCheck.Any())
{
return -1;
}
var childrenNodes = new List<string>();
level++;
for (int q = 0; q < nodesToCheck.Count; q++)
{
string[] options = bookFormat[Convert.ToInt32(nodesToCheck[q])-1].Split(' ');
if (options[0] == "0")
{
return level;
}
else
{
for (int t = 1; t < options.Length; t++)
{
if (!alreadyChecked.Contains(options[t]))
{
childrenNodes.Add(options[t]);
alreadyChecked.Add(nodesToCheck[q]);
}
}
}
nodesToCheck.Clear();
}
return getShortestPath(childrenNodes);
}
When nodesToCheck is empty, return -1 (i.e. no path).
Create a new List<string> for childrenNodes within the getShortestPath method.
While this should fix your problem, I would recommend making your entire method self-contained. It's essentially a stateless method, but you're maintaining state outside the method, which led to the problem you have seen, and could lead to more problems if you call this method in a multi-threaded environment.
The other odd thing I noticed, is that you're looping through nodesToCheck, but the way your code is written means that you will only ever consider the first node because you clear nodesToCheck at the end of the first iteration, leaving the list empty. Further more, you're adding nodesToCheck[q] to alreadChecked once per item in options, which I'm sure can't be right.
I recommend learning to use the debugger in Visual Studio. The debugger allows you to step through your code line-by-line, inspect variables, etc. - this will help you locate problems in your code.
P.S. While it is not the correct solution to your problem, if you wish to copy a list you can use LINQ's ToList() method: var listB = listA.ToList();
I've been trying to do the following:
public List<List<object[]>> Queue = new List<List<object[]>>();
Queue = InitList(MaxLayerCapability, new List<object[]>());
Having
public List<T> InitList<T>(int count, T initValue)
{
return Enumerable.Repeat(initValue, count).ToList();
}
So here is where the issue resides:
Queue[2].Add(new object[] { "Draw", "Test" });
for ( int i = 0; i < MaxLayerCapability; i++)
{
Console.WriteLine(i + ">" + Queue[i].Count);
//Operate(Queue[i], i);
}
For some reason, I want that Queue[2] to contains elements, and all other lists (for example, Queue[0]) should have a count of 0.
It is at some point pushing all the elements into Queue, any ideas?
Here's what I'm getting:
0>1
1>1
2>1
3>1
4>1
5>1
Thanks in advance.
The problem is, as i understand it, that Repeat just repeats reference to the same instance of List.
I would refactor it this way:
public IEnumerable<T> InitList<T>(int count)
{
for (int i=0;i<count; i++)
{
yield return Activator.CreateInstance<T>();
}
}
I see correct results if i use fucntion this way:
Queue = InitList<List<object[]>>(3).ToList();
What is the safest (and shortest) way do lock read/write access to static members in a multithreaded environment in C#?
Is it possible to do the threadsafe locking & unlocking on class level (so I don't keep repeating lock/unlock code every time static member access is needed)?
Edit: Sample code would be great :)
Edit: Should I use the volatile keyword or Thread.MemoryBarrier() to avoid multiprocessor caching or is that unnecessary? According to Jon Skeet only those will make changes visible to other processors? (Asked this separately here).
Small Values
For small values (basically any field that can be declared volatile), you can do the following:
private static volatile int backingField;
public static int Field
{
get { return backingField; }
set { backingField = value; }
}
Large Values
With large values the assignment won't be atomic if the value is larger then 32-bits on a 32-bit machine or 64-bits on a 64-bit machine. See the ECMA 335 12.6.6 spec. So for reference types and most of the built-in value types the assignment is atomic, however if you have some large struct, like:
struct BigStruct
{
public long value1, valuea0a, valuea0b, valuea0c, valuea0d, valuea0e;
public long value2, valuea0f, valuea0g, valuea0h, valuea0i, valuea0j;
public long value3;
}
In this case you will need some kind of locking around the get accessor. You could use ReaderWriterLockSlim for this which I've demonstrated below. Joe Duffy has advice on using ReaderWriterLockSlim vs ReaderWriterLock:
private static BigStruct notSafeField;
private static readonly ReaderWriterLockSlim slimLock =
new ReaderWriterLockSlim();
public static BigStruct Safe
{
get
{
slimLock.EnterReadLock();
var returnValue = notSafeField;
slimLock.ExitReadLock();
return returnValue;
}
set
{
slimLock.EnterWriteLock();
notSafeField = value;
slimLock.ExitWriteLock();
}
}
Unsafe Get-Accessor Demonstration
Here's the code I used to show the lack of atomicity when not using a lock in the get-accessor:
private static readonly object mutexLock = new object();
private static BigStruct notSafeField;
public static BigStruct NotSafe
{
get
{
// this operation is not atomic and not safe
return notSafeField;
}
set
{
lock (mutexLock)
{
notSafeField = value;
}
}
}
public static void Main(string[] args)
{
var t = new Thread(() =>
{
while (true)
{
var current = NotSafe;
if (current.value2 != (current.value1 * 2)
|| current.value3 != (current.value1 * 5))
{
throw new Exception(String.Format("{0},{1},{2}", current.value1, current.value2, current.value3));
}
}
});
t.Start();
for(int i=0; i<50; ++i)
{
var w = new Thread((state) =>
{
while(true)
{
var index = (int) state;
var newvalue = new BigStruct();
newvalue.value1 = index;
newvalue.value2 = index * 2;
newvalue.value3 = index * 5;
NotSafe = newvalue;
}
});
w.Start(i);
}
Console.ReadLine();
}
The safest and shortest way is to create a private, static field of type Object that is only used for locking (think of it as a "pad-lock" object). Use this and only this field to lock on as this prevent other types from locking up your code when then lock on the same type that you do.
If you lock on the type itself there is risk that another type will also decide to lock on your type and this could create deadlocks.
Here is an example:
class Test
{
static readonly Object fooLock = new Object();
static String foo;
public static String Foo
{
get { return foo; }
set
{
lock (fooLock)
{
foo = value;
}
}
}
}
Notice that I have create a private, static field for locking foo - I use that field to lock the write operations on that field.
Although you could just use a single mutex to control all the access to the class (effectively serializing the access to the class) I suggest you study the static class, determine which members are being used where and how and the use one or several ReaderWriterLock (code examples in the MSDN documentation) which provides access to several readers but only one writer at the same time.
That way you'll have a fine grained multithreaded class which will only block for writing but will allow several readers at the same time and which will allow writing to one member while reading another unrelated member.
class LockExample {
static object lockObject = new object();
static int _backingField = 17;
public static void NeedsLocking() {
lock(lockObject) {
// threadsafe now
}
}
public static int ReadWritePropertyThatNeedsLocking {
get {
lock(lockObject) {
// threadsafe now
return _backingField;
}
}
set {
lock(lockObject) {
// threadsafe now
_backingField = value;
}
}
}
}
lock on an object specifically created for this purpose rather than on typeof(LockExample) to prevent deadlock situations where others have locked on LockExample's type object.
Is it possible to do the threadsafe locking & unlocking on class level (so I don't keep repeating lock/unlock code every time static member access is needed)?
Only lock where you need it, and do it inside the callee rather than requiring the caller to do the locking.
Several others have already explained how to use the lock keyword with a private lock object, so I will just add this:
Be aware that even if you lock inside each method in your type, calling more than one method in a sequence can not be considered atomic. For example if you're implementing a dictionary and your interface has a Contains method and an Add method, calling Contains followed by Add will not be atomic. Someone could modify the dictionary between the calls to Contains and Add - i.e. there's a race condition. To work around this you would have to change the interface and offer a method like AddIfNotPresent (or similar) which encapsulates both the checking and the modification as a single action.
Jared Par has an excellent blog post on the topic (be sure to read the comments as well).
You should lock/unlock on each static member access, within the static accessor, as needed.
Keep a private object to use for locking, and lock as required. This keeps the locking as fine-grained as possible, which is very important. It also keeps the locking internal to the static class members. If you locked at the class level, your callers would become responsible for the locking, which would hurt usability.
I thank you all and I'm glad to share this demo program, inspired by the above contributions, that run 3 modes (not safe, mutex, slim).
Note that setting "Silent = false" will result in no conflict at all between the threads. Use this "Silent = false" option to make all threads write in the Console.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Test
{
class Program
{
//------------------------------------------------------------------------------
// Configuration.
const bool Silent = true;
const int Nb_Reading_Threads = 8;
const int Nb_Writing_Threads = 8;
//------------------------------------------------------------------------------
// Structured data.
public class Data_Set
{
public const int Size = 20;
public long[] T;
public Data_Set(long t)
{
T = new long[Size];
for (int i = 0; i < Size; i++)
T[i] = t;
}
public Data_Set(Data_Set DS)
{
Set(DS);
}
public void Set(Data_Set DS)
{
T = new long[Size];
for (int i = 0; i < Size; i++)
T[i] = DS.T[i];
}
}
private static Data_Set Data_Sample = new Data_Set(9999);
//------------------------------------------------------------------------------
// SAFE process.
public enum Mode { Unsafe, Mutex, Slim };
public static Mode Lock_Mode = Mode.Unsafe;
private static readonly object Mutex_Lock = new object();
private static readonly ReaderWriterLockSlim Slim_Lock = new ReaderWriterLockSlim();
public static Data_Set Safe_Data
{
get
{
switch (Lock_Mode)
{
case Mode.Mutex:
lock (Mutex_Lock)
{
return new Data_Set(Data_Sample);
}
case Mode.Slim:
Slim_Lock.EnterReadLock();
Data_Set DS = new Data_Set(Data_Sample);
Slim_Lock.ExitReadLock();
return DS;
default:
return new Data_Set(Data_Sample);
}
}
set
{
switch (Lock_Mode)
{
case Mode.Mutex:
lock (Mutex_Lock)
{
Data_Sample.Set(value);
}
break;
case Mode.Slim:
Slim_Lock.EnterWriteLock();
Data_Sample.Set(value);
Slim_Lock.ExitWriteLock();
break;
default:
Data_Sample.Set(value);
break;
}
}
}
//------------------------------------------------------------------------------
// Main function.
static void Main(string[] args)
{
// Console.
const int Columns = 120;
const int Lines = (Silent ? 50 : 500);
Console.SetBufferSize(Columns, Lines);
Console.SetWindowSize(Columns, 40);
// Threads.
const int Nb_Threads = Nb_Reading_Threads + Nb_Writing_Threads;
const int Max = (Silent ? 50000 : (Columns * (Lines - 5 - (3 * Nb_Threads))) / Nb_Threads);
while (true)
{
// Console.
Console.Clear();
Console.WriteLine("");
switch (Lock_Mode)
{
case Mode.Mutex:
Console.WriteLine("---------- Mutex ----------");
break;
case Mode.Slim:
Console.WriteLine("---------- Slim ----------");
break;
default:
Console.WriteLine("---------- Unsafe ----------");
break;
}
Console.WriteLine("");
Console.WriteLine(Nb_Reading_Threads + " reading threads + " + Nb_Writing_Threads + " writing threads");
Console.WriteLine("");
// Flags to monitor all threads.
bool[] Completed = new bool[Nb_Threads];
for (int i = 0; i < Nb_Threads; i++)
Completed[i] = false;
// Threads that change the values.
for (int W = 0; W < Nb_Writing_Threads; W++)
{
var Writing_Thread = new Thread((state) =>
{
int t = (int)state;
int u = t % 10;
Data_Set DS = new Data_Set(t + 1);
try
{
for (int k = 0; k < Max; k++)
{
Safe_Data = DS;
if (!Silent) Console.Write(u);
}
}
catch (Exception ex)
{
Console.WriteLine("\r\n" + "Writing thread " + (t + 1) + " / " + ex.Message + "\r\n");
}
Completed[Nb_Reading_Threads + t] = true;
});
Writing_Thread.Start(W);
}
// Threads that read the values.
for (int R = 0; R < Nb_Reading_Threads; R++)
{
var Reading_Thread = new Thread((state) =>
{
int t = (int)state;
char u = (char)((int)('A') + (t % 10));
try
{
for (int j = 0; j < Max; j++)
{
Data_Set DS = Safe_Data;
for (int i = 0; i < Data_Set.Size; i++)
{
if (DS.T[i] != DS.T[0])
{
string Log = "";
for (int k = 0; k < Data_Set.Size; k++)
Log += DS.T[k] + " ";
throw new Exception("Iteration " + (i + 1) + "\r\n" + Log);
}
}
if (!Silent) Console.Write(u);
}
}
catch (Exception ex)
{
Console.WriteLine("\r\n" + "Reading thread " + (t + 1) + " / " + ex.Message + "\r\n");
}
Completed[t] = true;
});
Reading_Thread.Start(R);
}
// Wait for all threads to complete.
bool All_Completed = false;
while (!All_Completed)
{
All_Completed = true;
for (int i = 0; i < Nb_Threads; i++)
All_Completed &= Completed[i];
}
// END.
Console.WriteLine("");
Console.WriteLine("Done!");
Console.ReadLine();
// Toogle mode.
switch (Lock_Mode)
{
case Mode.Unsafe:
Lock_Mode = Mode.Mutex;
break;
case Mode.Mutex:
Lock_Mode = Mode.Slim;
break;
case Mode.Slim:
Lock_Mode = Mode.Unsafe;
break;
}
}
}
}
}
Locking in static methods sounds like a bad idea, for one thing if you use these static methods from class constructor you could run into some interesting side-effects due to loader locks (and the fact that class loaders can ignore other locks).
The docs for both DynamicInvoke and DynamicInvokeImpl say:
Dynamically invokes (late-bound) the
method represented by the current
delegate.
I notice that DynamicInvoke and DynamicInvokeImpl take an array of objects instead of a specific list of arguments (which is the late-bound part I'm guessing). But is that the only difference? And what is the difference between DynamicInvoke and DynamicInvokeImpl.
The main difference between calling it directly (which is short-hand for Invoke(...)) and using DynamicInvoke is performance; a factor of more than *700 by my measure (below).
With the direct/Invoke approach, the arguments are already pre-validated via the method signature, and the code already exists to pass those into the method directly (I would say "as IL", but I seem to recall that the runtime provides this directly, without any IL). With DynamicInvoke it needs to check them from the array via reflection (i.e. are they all appropriate for this call; do they need unboxing, etc); this is slow (if you are using it in a tight loop), and should be avoided where possible.
Example; results first (I increased the LOOP count from the previous edit, to give a sensible comparison):
Direct: 53ms
Invoke: 53ms
DynamicInvoke (re-use args): 37728ms
DynamicInvoke (per-cal args): 39911ms
With code:
static void DoesNothing(int a, string b, float? c) { }
static void Main() {
Action<int, string, float?> method = DoesNothing;
int a = 23;
string b = "abc";
float? c = null;
const int LOOP = 5000000;
Stopwatch watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++) {
method(a, b, c);
}
watch.Stop();
Console.WriteLine("Direct: " + watch.ElapsedMilliseconds + "ms");
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++) {
method.Invoke(a, b, c);
}
watch.Stop();
Console.WriteLine("Invoke: " + watch.ElapsedMilliseconds + "ms");
object[] args = new object[] { a, b, c };
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++) {
method.DynamicInvoke(args);
}
watch.Stop();
Console.WriteLine("DynamicInvoke (re-use args): "
+ watch.ElapsedMilliseconds + "ms");
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++) {
method.DynamicInvoke(a,b,c);
}
watch.Stop();
Console.WriteLine("DynamicInvoke (per-cal args): "
+ watch.ElapsedMilliseconds + "ms");
}
Coincidentally I have found another difference.
If Invoke throws an exception it can be caught by the expected exception type.
However DynamicInvoke throws a TargetInvokationException. Here is a small demo:
using System;
using System.Collections.Generic;
namespace DynamicInvokeVsInvoke
{
public class StrategiesProvider
{
private readonly Dictionary<StrategyTypes, Action> strategies;
public StrategiesProvider()
{
strategies = new Dictionary<StrategyTypes, Action>
{
{StrategyTypes.NoWay, () => { throw new NotSupportedException(); }}
// more strategies...
};
}
public void CallStrategyWithDynamicInvoke(StrategyTypes strategyType)
{
strategies[strategyType].DynamicInvoke();
}
public void CallStrategyWithInvoke(StrategyTypes strategyType)
{
strategies[strategyType].Invoke();
}
}
public enum StrategyTypes
{
NoWay = 0,
ThisWay,
ThatWay
}
}
While the second test goes green, the first one faces a TargetInvokationException.
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SharpTestsEx;
namespace DynamicInvokeVsInvoke.Tests
{
[TestClass]
public class DynamicInvokeVsInvokeTests
{
[TestMethod]
public void Call_strategy_with_dynamic_invoke_can_be_catched()
{
bool catched = false;
try
{
new StrategiesProvider().CallStrategyWithDynamicInvoke(StrategyTypes.NoWay);
}
catch(NotSupportedException exc)
{
/* Fails because the NotSupportedException is wrapped
* inside a TargetInvokationException! */
catched = true;
}
catched.Should().Be(true);
}
[TestMethod]
public void Call_strategy_with_invoke_can_be_catched()
{
bool catched = false;
try
{
new StrategiesProvider().CallStrategyWithInvoke(StrategyTypes.NoWay);
}
catch(NotSupportedException exc)
{
catched = true;
}
catched.Should().Be(true);
}
}
}
Really there is no functional difference between the two. if you pull up the implementation in reflector, you'll notice that DynamicInvoke just calls DynamicInvokeImpl with the same set of arguments. No extra validation is done and it's a non-virtual method so there is no chance for it's behavior to be changed by a derived class. DynamicInvokeImpl is a virtual method where all of the actual work is done.