Indexing arrays with enums in C# - c#

I have a lot of fixed-size collections of numbers where each entry can be accessed with a constant. Naturally this seems to point to arrays and enums:
enum StatType {
Foo = 0,
Bar
// ...
}
float[] stats = new float[...];
stats[StatType.Foo] = 1.23f;
The problem with this is of course that you cannot use an enum to index an array without a cast (though the compiled IL is using plain ints). So you have to write this all over the place:
stats[(int)StatType.foo] = 1.23f;
I have tried to find ways to use the same easy syntax without casting but haven't found a perfect solution yet. Using a dictionary seems to be out of the question since I found it to be around 320 times slower than an array. I also tried to write a generic class for an array with enums as index:
public sealed class EnumArray<T>
{
private T[] array;
public EnumArray(int size)
{
array = new T[size];
}
// slow!
public T this[Enum idx]
{
get { return array[(int)(object)idx]; }
set { array[(int)(object)idx] = value; }
}
}
or even a variant with a second generic parameter specifying the enum. This comes quite close to what I want but the problem is that you cannot just cast an unspecific enum (be it from a generic parameter or the boxed type Enum) to int. Instead you have to first box it with a cast to object and then cast it back. This works, but is quite slow. I found that the generated IL for the indexer looks something like this:
.method public hidebysig specialname instance !T get_Item(!E idx) cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: ldfld !0[] EnumArray`2<!T, !E>::array
L_0006: ldarg.1
L_0007: box !E
L_000c: unbox.any int32
L_0011: ldelem.any !T
L_0016: ret
}
As you can see there are unnecessary box and unbox instructions there. If you strip them from the binary the code works just fine and is just a tad slower than pure array access.
Is there any way to easily overcome this problem? Or maybe even better ways?
I think it would also be possible to tag such indexer methods with a custom attribute and strip those two instructions post-compile. What would be a suitable library for that? Maybe Mono.Cecil?
Of course there's always the possibility to drop enums and use constants like this:
static class StatType {
public const int Foo = 0;
public const int Bar = 1;
public const int End = 2;
}
which may be the fastest way since you can directly access the array.

I suspect you may be able to make it a bit faster by compiling a delegate to do the conversion for you, such that it doesn't require boxing and unboxing. An expression tree may well be the simplest way of doing that if you're using .NET 3.5. (You'd use that in your EnumArray example.)
Personally I'd be very tempted to use your const int solution. It's not like .NET provides enum value validation anyway by default - i.e. your callers could always cast int.MaxValue to your enum type, and you'd get an ArrayIndexException (or whatever). So, given the relative lack of protection / type safety you're already getting, the constant value answer is appealing.
Hopefully Marc Gravell will be along in a minute to flesh out the compiled conversion delegate idea though...

If your EnumArray wasn't generic, but instead explicitly took a StatType indexer - then you'd be fine. If that's not desirable, then I'd probably use the const approach myself. However, a quick test with passing in a Func<T, E> shows no appreciable difference vs direct access.
public class EnumArray<T, E> where E:struct {
private T[] _array;
private Func<E, int> _convert;
public EnumArray(int size, Func<E, int> convert) {
this._array = new T[size];
this._convert = convert;
}
public T this[E index] {
get { return this._array[this._convert(index)]; }
set { this._array[this._convert(index)] = value; }
}
}

If you have a lot of fixed-size collections, then it would probably be easier to wrap up your properties in an object than a float[]:
public class Stats
{
public float Foo = 1.23F;
public float Bar = 3.14159F;
}
Passing an object around will give you the type safety, concise code, and constant-time access that you want.
And if you really need to use an array, its easy enough to add a ToArray() method which maps the properties of your object to a float[].

struct PseudoEnum
{
public const int INPT = 0;
public const int CTXT = 1;
public const int OUTP = 2;
};
// ...
String[] arr = new String[3];
arr[PseudoEnum.CTXT] = "can";
arr[PseudoEnum.INPT] = "use";
arr[PseudoEnum.CTXT] = "as";
arr[PseudoEnum.CTXT] = "array";
arr[PseudoEnum.OUTP] = "index";
(I also posted this answer at https://stackoverflow.com/a/12901745/147511)
[edit: oops I just noticed that Steven Behnke mentioned this approach elsewhere on this page. Sorry; but at least this shows an example of doing it...]

enums are supposed to be type safe. If you're using them as the index of an array, you're fixing both the type and the values of the enum, so you have no benefit over declaring a static class of int constants.

I don't believe there is any way to add an implicit conversion operator to an enum, unfortunately. So you'll have to either live with ugly typecasts or just use a static class with consts.
Here's a StackOverflow question that discusses more on the implicit conversion operator:
Can we define implicit conversions of enums in c#?

I'm not 100% familiar with C#, but I've seen implicit operators used to map one type to another before. Can you create an implicit operator for the Enum type that allows you to use it as an int?

Related

How to declare a custom alias for System.Int32[] in c# [duplicate]

I'm trying to do some serialization / deserialization stuff with a custom exception type. This type has a field defined as:
private object[] resourceMessageParams;
I've got all nice and strongly typed code with some Linq expression magic, but I want to go even further than that and do something like this:
using ResourceMessageParamsType = object[];//<-- "Identifier expected" error here
/*...*/
private ResourceMessageParamsType resourceMessageParams;
/*...*/
this.resourceMessageParams =
(ResourceMessageParamsType)serializationInfo.GetValue(
ReflectionHelper.GetPropertyNameFromExpression(() =>
resourceMessageParams), typeof(ResourceMessageParamsType));
Instead of this:
(object[])serializationInfo.GetValue(
ReflectionHelper.GetPropertyNameFromExpression(() =>
resourceMessageParams), typeof(object[]));
To accomodate for possible change of type of this field in the future, so one will have to change the type only once in alias definition. However, the compiler stops at object in using ResourceMessageType = object[]; complaining that an identifier is expected. Changing to Object[] helps somewhat, but this time the bracket is highlighted with the same error message.
Is there a way to define an alias for array type in c#?
You could define a class (or struct) called ResourceMessageParamsType and define implicit operators for casting to and from object[].
struct ResourceMessageParamsType
{
private object[] value;
private ResourceMessageParamsType(object[] value)
{
this.value = value;
}
public static implicit operator object[](ResourceMessageParamsType t)
{
return t.value;
}
public static implicit operator ResourceMessageParamsType(object[] value)
{
return new ResourceMessageParamsType(value);
}
}
Simply put, you can't "alias" array types.
You can work around it by encapsulating things in a struct, but that doesn't answer your question.
Update:
From the ECMA standard,
using-alias-directive:
using identifier = namespace-or-type-name ;
which clearly doesn't say anything about arrays being permitted.
(See page 100 for how namespace-or-type-name is defined.)
I would just derive my type from System.Array. If I'm interpreting this correctly What you are describing is a non-OO approach like you would use in plain C.
Update- I guess you can't subclass System.Array. Maybe there is a way around it.
using ResourceMessageParamsType = System.Array;
Not that I pretend to understand how this "safeguard[s] serialization code against possible changes in class definition" for you.
Interfaces would be a cleaner approach, and have you considered generics?
IMO Comprehensive Unit tests will ensure that if someone changes the aliased type, that all deserialization code still works.
Wrapping in a managed struct as user541686 rightly mentions, creates another level of indirection to what is ultimately still a managed array. You can alternatively use a fixed buffer in an unsafe struct to have it allocated all-in-one under your preferred typename:
public unsafe struct MyPreferredTypeName
{
public fixed int buffer[256];
//Further dereferencing not req'd: this memory is allocated inline in the struct.
}
However you then have no choice but to use unsafe blocks in your code that uses the struct (and if I recall correctly, also fixed blocks depending on your version of C#, I think < 9.0).
public void MyMethod(MyPreferredTypeName data) //you can alternatively use unsafe in this line
{
unsafe
{
fixed (MyPreferredTypeName* dataPtr = data)
{
dataPtr[6] = 79; //etc
}
}
}

Make value type behave as reference

What is the best practice to be able to assign values to value type via "reference"? In my case I need this to give possibility to assign values to ints, floats etc through out the in-app console. I dont want to give a reference to an object containing the value type as this wont make my console manager generic, and adding new commands will be a nightmare. Also I cannot use pointers, as unsafe mode is impossible in my case.
Cause my little understanding of CLR, I tried something like this:
int i = 1;
object o = i;
o = (object) 5;
Console.WriteLine(i);// prints original value of i, 1. I expected 5;
So it didnt work, so I found this class:
using System.Collections.Generic;
using System.Collections;
using System;
public class Pointer < T >
{
private Func<T> getter;
private Action<T> setter;
public Pointer ( Func<T> getter, Action<T> setter )
{
this.getter = getter;
this.setter = setter;
}
public T Value
{
get { return getter(); }
set { setter(value); }
}
}
Thanks to it I can assign values to value types like to reference like this:
int i = 1;
Pointer ptr = new Pointer<int>(()=>i, x=>{i=x;})
ptr.Value = 5;
Console.WriteLine(i);// prints as expected 5;
Works, but as you can see it is quite not elegant way (at least I think it is not).
So the question remains. What is the best practice in cases like this?
Cheers
Official answer:
You should never expose direct access to your members. The values you want to be modifyable should be in a container object (the model). All modifications of this model should be done by one authority (the controller).
Inofficial answer:
Using reflection, you'll be able to access even value type fields and get or set their values.
If this does not help I need a clearer understanding of what you want to achieve.
It's not c# way. The simplest decision is create class with static fields and change it.

Building generic method for List<Enum>

In my application, I have something like:
public enum Locations {
LocationA,
LocationB,
LocationC
}
private List<Locations> _myLocations;
public Int64 PackedLocations {
get {
return PackEnumList(this._myLocations);
}
}
So: an enum (backed by int), a List of those enum values, and a read-only property which returns the result of a method I've left out so far.
That method, PackEnumList, is meant to give me a 64-bit integer where each BIT denotes whether the corresponding enum value was selected or not in a list of unique enum values. So in my example above, if _myLocations has only one item: {Locations.LocationA}, the result would be 1 (binary: ...00001), if we then add Locations.LocationC to that list, the result would be 5 (binary: ...000101). The implementation isn't important right now (but I'll include it below for completion/interest/feedback), but the signature of that method is:
public Int64 PackEnumList(List<Enum> listOfEnumValues) {
...
}
When I compile, I get an error that "the best overloaded method ... has some invalid arguments".
I'm guessing this is because _myLocations is being seen as a List of int values, but I'd like PackEnumList() to work even if the enumeration being used were backed by something else, if possible.
Is there a more appropriate way to make a method which will accept a List/Collection of any enumeration?
For completeness, here's the rest of what I'm trying to do (these are static because they're in a shared utility class). These are completely untested yet (since I can't get past the compile error when calling the pack method), so take them with a grain of salt. And there might be a better way to do this, I'm doing this half to solve an interesting problem, and half because I think it is an interesting problem.
public static Int64 PackEnumList(List<Enum> listOfEnumValues) {
BitArray bits = new BitArray(64, defaultValue: false);
foreach (var value in listOfEnumValues) {
// get integer value of each Enum in the List:
int val = Convert.ToInt32(value);
if (val >= 64) {
// this enum has more options than we have bits, so cannot pack
throw new Exception("Enum value out of range for packing: " + val.ToString());
}
bits[val] = true;
}
var res = new Int64[1];
bits.CopyTo(res, 0);
return res[0];
}
// (this method is a little farther from the ideal: the resulting list will need
// to be matched by the caller to the appropriate List of Enums by casting
// each Int32 value to the Enum object in the list)
public static List<Int32> UnpackEnumList(Int64 packedValue) {
string binaryString = Convert.ToString(packedValue, 2);
List<Int32> res = new List<Int32>();
for (int pos = 0; pos < binaryString.Length; pos++) {
if (binaryString[binaryString.Length - pos - 1] == '1') {
// bit is on
res.Add(pos);
}
}
return res;
}
Is there a more appropriate way to make a method which will accept a List/Collection of any enumeration?
Within straight C#? Nope. But you can fudge it...
I have a project called Unconstrained Melody which allows you to make a generic method with a constraint of "T must be an enum type" or "T must be a delegate type". These are valid constraints at the IL level, but can't be expressed in C#
Basically Unconstrained Melody consists of two parts:
A library of useful methods with those constraints, where the source code is written using valid C# which doesn't actually represent those constraints, but uses a marker interface
An IL-rewriting project (ugly but servicable) which converts those constraints into the real "unspeakable" ones
(The expectation is that users of the library would just use the rewritten binary.)
It sounds like you could use the latter part of the project for your code here. It won't be terribly pleasant, but it would work. You might also find the library part useful.
As a side thought, you might want to consider using a [Flags]-style enum instead:
[Flags]
public enum Locations {
LocationA = 1 << 0,
LocationB = 1 << 1,
LocationC = 1 << 2
}
Change your method signature to public Int64 PackEnumList(IEnumerable<Enum> listOfEnumValues)
And then call it like following:
public Int64 PackedLocations
{
get { return PackEnumList(this._myLocations.Cast<Enum>()); }
}
A List<Enum> is not a List<Locations> nor a List<Int32>. Use a generic method to handle the list:
public static void PackEnumList<T>(IEnumerable<T> list) where T : IConvertible
{
foreach (var value in list)
int numeric = value.ToInt32();
// etc.
}
I'd change your signature method to:
public Int64 PackEnumList<T>(IEnumerable<T> listOfEnumValues) where T : struct, IFormattable, IConvertible {
...
}
The where T : struct... constrains it to enum types only (any any other struct implementing both interfaces, which is probably very low)

Is it possible to alias array type in c#?

I'm trying to do some serialization / deserialization stuff with a custom exception type. This type has a field defined as:
private object[] resourceMessageParams;
I've got all nice and strongly typed code with some Linq expression magic, but I want to go even further than that and do something like this:
using ResourceMessageParamsType = object[];//<-- "Identifier expected" error here
/*...*/
private ResourceMessageParamsType resourceMessageParams;
/*...*/
this.resourceMessageParams =
(ResourceMessageParamsType)serializationInfo.GetValue(
ReflectionHelper.GetPropertyNameFromExpression(() =>
resourceMessageParams), typeof(ResourceMessageParamsType));
Instead of this:
(object[])serializationInfo.GetValue(
ReflectionHelper.GetPropertyNameFromExpression(() =>
resourceMessageParams), typeof(object[]));
To accomodate for possible change of type of this field in the future, so one will have to change the type only once in alias definition. However, the compiler stops at object in using ResourceMessageType = object[]; complaining that an identifier is expected. Changing to Object[] helps somewhat, but this time the bracket is highlighted with the same error message.
Is there a way to define an alias for array type in c#?
You could define a class (or struct) called ResourceMessageParamsType and define implicit operators for casting to and from object[].
struct ResourceMessageParamsType
{
private object[] value;
private ResourceMessageParamsType(object[] value)
{
this.value = value;
}
public static implicit operator object[](ResourceMessageParamsType t)
{
return t.value;
}
public static implicit operator ResourceMessageParamsType(object[] value)
{
return new ResourceMessageParamsType(value);
}
}
Simply put, you can't "alias" array types.
You can work around it by encapsulating things in a struct, but that doesn't answer your question.
Update:
From the ECMA standard,
using-alias-directive:
using identifier = namespace-or-type-name ;
which clearly doesn't say anything about arrays being permitted.
(See page 100 for how namespace-or-type-name is defined.)
I would just derive my type from System.Array. If I'm interpreting this correctly What you are describing is a non-OO approach like you would use in plain C.
Update- I guess you can't subclass System.Array. Maybe there is a way around it.
using ResourceMessageParamsType = System.Array;
Not that I pretend to understand how this "safeguard[s] serialization code against possible changes in class definition" for you.
Interfaces would be a cleaner approach, and have you considered generics?
IMO Comprehensive Unit tests will ensure that if someone changes the aliased type, that all deserialization code still works.
Wrapping in a managed struct as user541686 rightly mentions, creates another level of indirection to what is ultimately still a managed array. You can alternatively use a fixed buffer in an unsafe struct to have it allocated all-in-one under your preferred typename:
public unsafe struct MyPreferredTypeName
{
public fixed int buffer[256];
//Further dereferencing not req'd: this memory is allocated inline in the struct.
}
However you then have no choice but to use unsafe blocks in your code that uses the struct (and if I recall correctly, also fixed blocks depending on your version of C#, I think < 9.0).
public void MyMethod(MyPreferredTypeName data) //you can alternatively use unsafe in this line
{
unsafe
{
fixed (MyPreferredTypeName* dataPtr = data)
{
dataPtr[6] = 79; //etc
}
}
}

Which one is better to use and why in c#

Which one is better to use?
int xyz = 0;
OR
int xyz= default(int);
int xyz = 0;
Why make people think more than necessary? default is useful with generic code, but here it doesn't add anything. You should also think if you're initializing it in the right place, with a meaningful value. Sometimes you see, with stack variables, code like:
int xyz = 0;
if(someCondition)
{
// ...
xyz = 1;
// ...
}
else
{
// ...
xyz = 2;
// ...
}
In such cases, you should delay initialization until you have the real value. Do:
int xyz;
if(someCondition)
{
// ...
xyz = 1;
// ...
}
else
{
// ...
xyz = 2;
// ...
}
The compiler ensures you don't use an uninitialized stack variable. In some cases, you have to use meaningless values because the compiler can't know code will never execute (due to an exception, call to Exit, etc.). This is the exception (no pun intended) to the rule.
It depends what you want to achieve.
I would prefer
int xyz = 0;
as I believe it is more readable and not confusing.
default keyword is mostly suitable for Generics.
The purpose of the default operator is to provide you with the default value for a type, but it was added primarily to allow generics to have a valid value for values declared to be of its generic type arguments.
I don't have hard evidence but I suspect the compiler will emit the same code for both in your specific case.
However, here there is a legitimate use of default:
public T Aggregate<T>(IEnumerable<T> collection, Func<T, T, T> aggregation)
{
T result = default(T);
foreach (T element in collection)
result = aggregation(result, element);
return result;
}
Without default, the above code would need some hacks in order to compile and function properly.
So use the first, set it to 0.
no performance difference between your codes. to see clearly use int xyz = 0;
Given that the emitted CIL is identical (you get
IL_0001: ldc.i4.0
IL_0002: stloc.0
in both cases), the rule is to choose the one you feel better communicates the intent of the code. Normally, questions of feeling are subjective and hard-to-decide; in this case, however, were I the code reviewer, I would have to be presented with an extremely compelling reason to accept what looks at first sight to be an entirely superfluous use of default().
int xyz = default(int);
I like this way when working with Generics bcoz it give you flexibility to get default of whatever type you are working with.
int xyz=0;
On the other hand this is easy and fast and obviously won't work in generic cases.
Both have their pros and cons..
Regards,
int xyz = 0 is moreclear, defaut is generally used with generics
the best is
int xyz;
because you can't access to uninitialized variable.

Categories

Resources