Ok, so when I have a method that looks like
getPacket(params object[] inputs)
{
}
Is the inputs array an array of boxed variables or is it simply just an array of the original types (im sending multiple different types tho, eg. short, int, bool)
If they are boxed can you do run-time unboxing to the original type without knowing the original type?
If they aren't boxed, how can I tell whether it's an int, short, bool etc. as I want to be able to make a single method that puts together a byte array from a whole stack of different types.
Another question, are the objects in the array in the same order as they were passed in the method invocation?
The objects in the array will be in the same order that they were passed to the method and they will be boxed if the source parameter is a value type.
You can use the is keyword to check the underlying type of each object and act accordingly, for example:
static void Main(string[] args)
{
Receive(1, true);
}
static void Receive(params object[] values)
{
foreach (var v in values)
{
if (v is int)
{
// ...
}
else if (v is bool)
{
// ...
}
}
}
if you have an array of objects, then yes, if for example you put integers in the array, they will be boxed. I dont know of any unboxing methods without knowing the actual types involved, so you will have to cast the items in the array to whatever value they actually represent.
Related
I need to read a tab (or comma) separated text and assign them to a list of built-in variables (int, char, string, etc. but not object). I want to handle this with a generic method. Here is the working code I am using for the case with three variables.
public static void ReadVariablesFromString<T0, T1, T2>(out T0 input0, out T1 input1, out T2 input2, string stringIn, char[] separators)
{
string[] stringParts = stringIn.Split(separators);
input0 = ConvertTo<T0>(stringParts[0]);
input1 = ConvertTo<T1>(stringParts[1]);
input2 = ConvertTo<T2>(stringParts[2]);
}
public static T ConvertTo<T>(object value)
{
return (T)Convert.ChangeType(value, typeof(T));
}
When I want to assign the tab separated text to three variables of different type, this is what I use:
string str = "a\t123\tbcd";
char var1;
int var2;
string var3;
char[] separators = { '\t' };
ReadVariablesFromString(out var1, out var2, out var3, str, separators);
Is there a way to generalize this model for different number of variables? Is it possible to write a generic method that accepts different number of variables for this specific case?
One of the other solution might be writing this method for a variable list with different types. Instead of passing variables one by one, is there a way to pass them in a list?
No, in C# you can not send array of variable references.
But what you can do is create a class with some properties, and then use reflection to fill them.
See Enumerating through an object's properties (string) in C# and Set object property using reflection
and How to dynamically cast an object of type string to an object of type T on some details.
I suggest you return a List
List<object> variables = ReadVariablesFromString(str, separators);
Where each entry in the list will be the values your parse from the input string, for example
variables.ElementAt(0) will be 'a'
Since the type of the list is 'object', you can store any data type in there, since each type ultimately inherits from object anyway. You would need to cast the data when you need to use it though
char letter = (char)variables.ElementAt(0)
But you must be doing something similar already, otherwise how would you know which variable to put in each incoming out parameter.
Doing this means you have more flexibility in the amount of variables you can return from ReadVariablesFromString()
I m trying to use object handlers and I have this working fine to put stuff in memory. But when I look up the object again I return:
object(object[,])
or
object(double[,])
how do I unbox this in c#?
object(double[,]) boxedobj = ....
double[,] unboxedobj = unbox(boxedobj);
Ideally I would like to do this in a generic way so that it doesnt matter whether the tybe is double[] or double[,] or object[,], etc
The F# unbox function is pretty much just doing cast to any other type that you specify. In C#, this could be written like this:
static R Unbox<R>(object anything) {
return (R)anything;
}
So in your case with double[,] you'd need something like:
var array = (double[,])boxed;
In most cases unbox<'R> anything would just translate to casting using (R)anything. The only case where this does not work is when you are in another generic method and you are casting between two generic types. In that case, you need to go to object first (to make sure that the value is boxed):
static R Unbox<R, T>(T anything) {
return (R)(object)anything;
}
Unless I'm missing your point, casting to/from object should automatically box/unbox value types for you (an array is not a value type, BTW):
double d = 0.0; // not boxed
object obj = d; // boxed
double d2 = (double)obj; // unboxed
The bigger question is - why do you care if its boxed or not?
The method below looks for a particular key in a dictionary and attempts to safely store it in destination if it can. The problem I am running into is when T=Int64 and the item in the dictionary is Int32 or UInt32 (both of which can fit inside an Int64 without data loss). valueAsObject is T returns false when T is Int64 and valueAsObject is Int32.
Is it possible to safely cast valueAsObject to T without just trying it and catching any exceptions? I would like to avoid exceptions, though if that is the only way I can make it work.
public static void MyMethod<T>(IDictionary<String, Object> dictionary, String key, ref T destination)
{
if (!dictionary.ContainsKey(key))
return;
var valueAsObject = dictionary[key];
if (!(valueAsObject is T))
return;
destination = (T)valueAsObject;
}
Basically, I want the function to do nothing (return) if the thing found in the dictionary can not be safely stored in a type T variable (without data loss), otherwise it should cast it as necessary and store it.
This is not built-in at the language or runtime level. You can use Convert.ChangeType to perform the conversion. This method also performs lossy conversions so you probably have to build your own conversion logic (which will involve casts and be ugly).
You can't just do something like this?
public static void MyMethod<T>( IDictionary<string,object> dictionary , string key , ref T destination )
{
object value ;
bool found = dictionary.TryGetValue( key , out value ) ;
if (found && value is T)
{
destination = (T) value ;
}
return;
}
The is operator:
evaluates to true if the provided expression is non-null, and the provided object
can be cast to the provided type without causing an exception to be thrown.
.
.
.
Note that the is operator only considers reference conversions, boxing conversions,
and unboxing conversions. Other conversions, such as user-defined conversions, are not considered.
Basically, if the object directly inherits from the specified type, or implements the type (if T is an interface), is should return true and you should be able to down-cast the object to your type T.
If, however, you need to worry about user-defined conversion operators, you'll need to reflect over the object's type and over typeof(T) to see if a user-defined conversion operator is defined on either type that will convert the object to a compatible type. Getting that right is likely to be...tricky.
This is the final solution I went with. It came from a number of comments and other answers so I encourage reading the other answers/comments for additional details as to why this was necessary:
private static void GetFromDictionary<T>(IDictionary<String, Object> dictionary, String key, ref T outputLocation)
{
if (!dictionary.ContainsKey(key))
return;
var valueAsObject = dictionary[key];
if (!CanBeCastTo<T>(valueAsObject))
return;
outputLocation = (T)Convert.ChangeType(valueAsObject, typeof(T));
}
private static bool CanBeCastTo<T>(Object thingToCast)
{
if (thingToCast is T)
return true;
var tType = typeof(T);
if (tType == typeof(Int64) && thingToCast is Int32)
return true;
if (tType == typeof(Double) && thingToCast is Decimal)
return true;
return false;
}
In my case, I only needed to handle a certain subset of primitive types (Int32, Int64, String, Double, Decimal) and I was willing to accept lossy conversion of Decimal to Double.
The take away here is that the "is" operator will return false for primitives even if it can fit without loss (Int32 in Int64). Also, (T)valueAsObject will not do an explicit typecast. So if valueAsObject is of type Int32 and T is of type Int64 the above will throw an exception even though (Int64)valueAsObject is valid. This one bit me but luckily #usr suggested using Convert.ChangeType which handles the problem.
Optimizations could be made to the above code but I chose to leave it as is for readability.
When a List<> of primitive types is created in C# (e.g. a List<int>), are the elements inside the list stored by value, or are they stored by reference?
In other words, is C# List<int> equivalent to C++ std::vector<int> or to C++ std::vector<shared_ptr<int>>?
A List<int> will have an int[] internally. No boxing is required usually - the values are stored directly in the array. Of course if you choose to use the List<T> as a non-generic IList, where the API is defined in terms of object, that will box:
List<int> list1 = new List<int>();
// No boxing or unboxing here
list1.Add(5);
int x = list1[0];
// Perfectly valid - but best avoided
IList list2 = new List<int>();
// Boxed by the caller, then unboxed internally in the implementation
list2.Add(5);
// Boxed in the implementation, then unboxed by the caller
int y = (int) list2[0];
Note that the phrase "stored by reference" is a confusing one - the term "by reference" is usually used in the context of parameter passing where it's somewhat different.
So while a List<string> (for example) contains an array where each element value is a reference, in a List<int> each element value is simply an int. The only references involved are the callers reference to the List<int>, and the internal reference to the array. (Array types themselves are always reference types, even if the element type is a value type.)
Value types are stored by value. (e.g. primitives and structs) Reference types are stored by reference. (e.g. classes)
So what would happen if you wrote code like this:
struct MutableValueType
{
public int ChangableInt32;
}
static class Program
{
static void Main()
{
var li = new List<MutableValueType>();
li.Add(new MutableValueType());
li[0].ChangableInt32 = 42;
}
}
Will you modify a copy of your struct, or will you change the copy that is inside the List<>? Will the compiler warn you? I would like to try this.
Is there a way to create a method that gets an enum type as a parameter, and returns a generic list of the enum underlying type from it's values, no matter if the underlying type is int\short byte etc'...
I saw this answer of Jon Skeet, but it looks way too complicated.
If you want to pass in a Type, it can't really be usefully generic - you'd have to return a single type that isn't directly related to the input, hence something like:
public static Array GetUnderlyingEnumValues(Type type)
{
Array values = Enum.GetValues(type);
Type underlyingType = Enum.GetUnderlyingType(type);
Array arr = Array.CreateInstance(underlyingType, values.Length);
for (int i = 0; i < values.Length; i++)
{
arr.SetValue(values.GetValue(i), i);
}
return arr;
}
This is a strongly-typed vector underneath, so you could cast that to int[] etc.
While Marc's answer isn't wrong its somewhat unnecessary.
Enum.GetValues(type) returns a TEnum[] so this method is kind of unnecessary as if you know the underlying type you can just cast TEnum[] to its underlying type array.
var underlyingArray = (int[])Enum.GetValues(typeof(StringComparison));
is valid C# that will compile and won't throw an exception at runtime. Since you wanted a list once you have the array you can pass it to the List<Tunderlying> constructor or you can just call the ToArray() extension method.
Edit: you could write the function as such::
public static TUnderlying[] GetValuesAs<TUnderlying>(type enumType)
{
return Enum.GetValues(enumType) as TUnderlying[];
}
But then you would have to know the underlying type first.