TargetParameterCountException when iteratively retrieving value out of reflexive properties of a string - c#

Some context first :
I was writing a reusable "null tester" method that will be used to check if any variable of an object has a null or empty value. the goal is that the method can take an Object and use reflexion to check the value of each property.
Everything worked until I added a List<string> to a class that is being checked.
While adding List<string> raised the TargetParameterCountException other primitive types didn't. The only other time I managed to reproduce this error is when I passed directly a string.
I managed to boil down this problem to that bit of code :
string toCheck = "foo";
var elementProperties = toCheck.GetType().GetProperties();
foreach (PropertyInfo property in elementProperties)
{
var elementValue = property.GetValue(toCheck);
//Check if "toCheck" is null or empty here
}
elementProperties has two Values
An Int32 Length
A Char Chars[Int32]
My understanding would be that the first represent the length and the second the content of the string. But when the code tries to "GetValue()" the second property it raises a TargetParameterCountException.
Does someone know why it would do that ?

since I was trying to access a char array I needed to specify the index of which char I want to retrieve.
using :
property.GetValue(toCheck, new object[] {index})

Related

TryGetValue Evaluating to False Even When Key is Present

This question is basically the same as this one, although the answer to that person's problem turned out to be a simple trailing space.
My issue is that I'm retrieving data from a web API as dictionary and then trying get the values out of it. I'm using TryGetValue because not every item in the dictionary will necessarily contain every key. For some reason, whilst I can get the value of one key with no problems at all when it's present, for another key TryGetValue always evaluates to false and therefore doesn't return the value, even though I can see in debug that the key is present.
So, this block always retrieves the value of the "System.Description" key if it's present:
string descriptionValue = "";
if (workItem.Fields.TryGetValue("System.Description", out descriptionValue))
{
feature.Description = descriptionValue;
}
However, this almost identical block NEVER retrieves the value of the "CustomScrum.RoadmapGroup" key:
int RoadmapGroupValue = 0;
if (workItem.Fields.TryGetValue("CustomScrum.RoadmapGroup", out RoadmapGroupValue))
{
feature.RoadmapGroup = RoadmapGroupValue;
}
As you can see in this screenshot, the dictionary DOES contain a key with a name exactly matching my TryGetValue statement:
If I put a breakpoint on the code which should be run if the TryGetValue statement evaluates to true (feature.Description = descriptionValue;) it never gets hit.
The feature.RoadmapGroup variable gets set to 0 for every item in the dictionary.
I've been staring at this for the last two hours at least and I can't see what I'm doing wrong.
Here's a scenario where your cast goes wrong.
private void foo()
{
Dictionary<string, object> dict = new Dictionary<string, object>();
object obj = new object();
obj = "1";
dict.Add("CustomScrum.RoadmapGroup", obj);
object val;
var result = dict.TryGetValue("CustomScrum.RoadmapGroup", out val);
int value = (int)val;
}
TryGetValue() returns true, but the last line (the cast), throws System.InvalidCastException: 'Specified cast is not valid.', although if you use a breakpoint to see the dictionary content it looks like you have something that can be converted to an int. See below:
So I believe that when you add the value to the dictionary, you're not really adding an int but something that looks like an int.
EDIT
I just replaced int value = (int)val; with int value = Convert.ToInt32(val); which converts the value just fine. So you might want to try to use that and see if that works as well.
Are you sure that this "CustomScrum.RoadmapGroup" key is a string? If yes, then make sure that it doesn't contain any special unreadable character. You can just copy this value while debugging, put it in Watch window and check length/bytes representation, then do the same for hand-written string with the same content.

How to get a list of mutable strings?

I have the following piece of code
List<String> l = new List<String>();
String s = "hello";
l.Add(s);
s = "world";
When I set up some breakpoints and go through the program, after executing the last line, the value in the list is still hello instead of world.
Shouldn't it equal world ? Isn't a string an object, and am I not just inserting a pointer into the list? Later on if I change the string to point to a different value ("world"), why is my list still referencing the old value?
How can I get my desired effect ?
Thanks a lot!
Strings are immutable so that won't work. When you attempt to set into it, you actually drop the pointer to the old string and create a new one under the hood.
To get the desired effect, create a class that wraps a string:
public class SortOfMutableString
{
public string Value {get;set;}
public SortOfMutableString(string s)
{
Value = s;
}
public static implicit operator string(SortOfMutableString s)
{
return s.Value;
}
public static implicit operator SortOfMutableString(string s)
{
return new SortOfMutableString(s);
}
}
And use this in your list. Then references will point to the class, but you can contain the string value inside. To make it even better, override implicit casting to and from string so you don't even need to see that you are talking to a SortOfMutableString.
Refer to Jon Skeet's answer for undoubtedly a very accurate explanation about string's in C#, I'm not even going to bother!
Alternative class names:
PseudoMutableString
ICantBelieveItsNotMutable
HappyAndReferenceableString
You're changing the s reference to refer to a different String instance.
Strings are immutable; it is impossible to change the existing instance that you added to the list.
Instead, you can create a mutable StringHolder class with a writable String property.
No, it shouldn't equal world. The value of the variable s is a reference. When you call l.Add(s), that reference is passed by value to the list. So the list now contains a reference to the string "hello".
You now change the value of s to a reference to the string "world". That doesn't change the list at all.
It's important to distinguish between three very different concepts:
A variable (which has a name and a value)
A reference (a value which allows you to navigate to an object, or null)
An object
So in particular, the list doesn't know anything about the variable s - it knows about the value which was passed into Add; that value happened to be the value of s at the time Add was called, that's all.
You may find these articles helpful:
Values and references
Parameter passing in C#
No, there are two different references involved. One called s and one that's at List[0]. When you say l.Add(s) you are setting the list reference to the same address as s, but then when you assign s to "world", then s will point to the new string, leaving List[0] pointing to the old string.
If you really want to do something like what you are asking, you'd need to wrap the string in another object that contains a string, so that s and List[0] both refer to that object, and then that object's reference to a string can change and both will see it.
public class StringWrapper
{
public string TheString { get; set; }
}
Then you can do:
var s = new StringWrapper { TheString = "Hello" };
var l = new List<StringWrapper>();
l.Add(s);
s.TheString = "World";
And now l[0].TheString will be world too. This works because in this case we are not changing the reference in List[0] or s, but they contents of the object referred to by s and List[0].
A variable is an object reference, not an object itself. s = "world" says "make s refer to the string "World") - it does not in any way affect the string "hello" that s was previously referring to. Furthermore, strings in C# are always immutable. You can, however, make the first list element (which currently refers to "hello") refer to a different string: l[0] = "world".
The other two answers here did a great job of saying why what you tried didnt' work, but you were looking for a solution for your desired effect. Wrap a string (property) inside of an object. Then you can change that string and it will be reflected in the collection.

How to check if key pair in 2D array exists?

I have this 2d array or struct
public struct MapCell
{
public string tile;
}
public MapCell[,] worldMap;
But there's no way to check if key pair is exists in this array or not... No methods for that available.
I tried to do it like this
if (worldMap[tileX, tileY] != null) {
}
it doesnt work:
Error 1 Operator '!=' cannot be applied to operands of type 'Warudo.MapCell' and '<null>'
and for
if (worldMap[tileX, tileY].tile != null) {
it doesn't work either (exception pops up when it hits non existing element).
Index was outside the bounds of the array.
So, how do I check if key pair is exists or not?
You never mentioned which error you are getting -- array out of bounds or a null reference. If you are getting array out of bounds you should precede your null check with something along the lines of...
// make sure we're not referencing cells out of bounds of the array
if (tileX < arr.GetLength(0) && tileY < arr.GetLength(1))
{
// logic
}
Of course, it's best to just store the maximum array bounds instead of getting their lengths each time.
I also second (third?) the recommendation for using a class and not a struct.
Edit: Are you ever actually initializing this field? You haven't included it in your code sample. For example worldMap = new MapCell[100,100];, and then fill up the array...
If you're using an array of struct values, they always exist (once the array is constructed), but have their default value until you set them.
I would recommend using a class here instead of a struct. This will allow you to check for null, as well as act more in an expected fashion if you're going to be changing values (which, given the names, I expect...)
That being said, you could check for whether the string within the struct is null:
if (worldMap[tileX, tileY].tile != null)
{
// You've set the "tile" field inside of this "cell"...
This works because the default value of a struct is initialized with all references, including strings, to null.

Length of string array via reflection

Is it possible to know the length of a string array - without having an object instance - via reflection?
E.g. in this case: 2.
public string[] Key
{
get { return new string[] { Name, Type }; }
}
EDIT: ok, I will not try to do this, it doesn't make much sense.
Perhaps you mean "without having the exact type of the Array". C# Arrays all derive from Array, so you can cast an Array reference to Array and use the Length property.
If you TRULY wants to reflect the property,
var type = typeof(MyClass);
var prop = type.GetProperty("Key");
var method = prop.GetGetMethod();
var body = method.GetMethodBody();
var ils = body.GetILAsByteArray();
from here you'll have to use one of the various libraries to decode bytes to IL OpCodes (for example https://gist.github.com/104001) . The OpCode you are looking for is newarr. The last push of an int32 before the newarr is the size of the array.
You have two things going on there... telling the length of an array is pretty simple once you have an array; you just call .Length (in the case of a vector).
However, you mention an instance, and you are showing an instance property; which makes me think it is the containing object you lack. In which case... no. You can't make a virtcall on a null instance. And trying to use static-call on an instance member of a class is very evil; IIRC the runtime will kick you for this.
You could, however, make it a static property just by adding the static modifier. Then you just pass in null as the instance to reflection.
I guess you mean you want to know the size of the array the property will return if it were called?
I don't think you can do it sensibly.
If the property had a conditional then it could return different sized arrays, so
you'd have to evaluate the property to know the size. Which could have side effects or be dependent on other values in the object (or statics).
Consider this one:-
static public int n;
public string[] Key
{
get {
if (n > 1)
return new string[] { "Name", "Type" };
else
return new string[] { "Name", "Type", "Cheese" };
}
}
Basically, you'd have to run the code.

C# Calling a Method, and variable scope

Why is cards being changed below? Got me puzzled.. understand passing by ref which works ok.. but when passing an Array is doesn't do as I expect. Compiling under .NET3.5SP1
Many thanks
void btnCalculate_Click(object sender, EventArgs e)
{
string[] cards = new string[3];
cards[0] = "old0";
cards[1] = "old1";
cards[2] = "old2";
int betResult = 5;
int position = 5;
clsRules myRules = new clsRules();
myRules.DealHand(cards, betResult, ref position); // why is this changing cards!
for (int i = 0; i < 3; i++)
textBox1.Text += cards[i] + "\r\n"; // these are all new[i] .. not expected!
textBox1.Text += "betresult " + betResult.ToString() + "\r\n"; // this is 5 as expected
textBox1.Text += "position " + position.ToString() + "\r\n"; // this is 6 as expected
}
public class clsRules
{
public void DealHand(string[] cardsInternal, int betResultInternal, ref int position1Internal)
{
cardsInternal[0] = "new0";
cardsInternal[1] = "new1";
cardsInternal[2] = "new2";
betResultInternal = 6;
position1Internal = 6;
}
}
Arrays are reference types which in short means the value of the array is not directly contained within a variable. Instead the variable refers to the value. Hopefully the following code will explain this a bit better (List<T> is also a reference type).
List<int> first = new List<int>()( new int[] {1,2,3});
List<int> second = first;
first.Clear();
Console.WriteLine(second.Count); // Prints 0
In this scenario there is a List<int> created on the first line which is referred to by variable first. The second line does not create a new list but instead creates a second variable named second which refers to the same List<int> object as first. This logic applies to all reference types.
When you pass the variable cards into the method you do not pass a copy of the full array but instead a copy of the variable cards. This copy refers to the same array object as the original cards. Hence any modifications you make to the array are visible through the original reference.
A variable of a reference type does
not contain its data directly; it
contains a reference to its data. When
you pass a reference-type parameter by
value, it is possible to change the
data pointed to by the reference, such
as the value of a class member.
However, you cannot change the value
of the reference itself; that is, you
cannot use the same reference to
allocate memory for a new class and
have it persist outside the block. To
do that, pass the parameter using the
ref or out keyword.
http://msdn.microsoft.com/en-us/library/s6938f28(VS.80).aspx
When you are passing a reference type (like an array) to a method by value, you are passing a copy of it's reference. It's still the same object that is referenced, it doesn't create a copy of the array itself.
When passing parameters to methods, there are three different concepts to be aware of:
By Value vs By Reference parameters
Value vs Reference types
Mutable vs Immutable types
In your example, the string array is a Reference type, is a Mutable type, and is passed By Value. The compiler will always let you change the content of the array because it is Mutable. However, since it is a Reference type, the calling code and the called code both point to the same array contents, so the calling code "sees the changes". The fact that it's passed by value in this case is irrelevant, since although the called code's array variable has indeed been passed a copy of the calling code's variable, they both point to the same location in memory.
As other answers have said, it's because a reference is being passed by value.
I have an article on argument passing in C# which you may find useful, in addition to the answers here.
Arrays are reference types, thus are subject to change.
When you are passing an array as an object it is not copied. The receiving method works with the same instance. In a sense arrays are always passed by ref. When an array as well as an instance of any other reference type is passed as a parameter the receiving method gets its own copy of a reference on the same instance of the type. No copy of the actual object is created.
If you need to pass a copy you have to be explicit about this: create a copy yourself or clone the array. The reason it is not done for you is obvious - copying an array can be expensive, you do not want it unless it is really necessary

Categories