I have a list of enums as follows:
public enum EventID : uint
{
SAMPLE_EVENT_1 = 0xDCCA0000,
SAMPLE_EVENT_2 = 0xDCCB0001,
SAMPLE_EVENT_3 = 0xDCCA0002,
SAMPLE_EVENT_4 = 0xDCC00003,
SAMPLE_EVENT_5 = 0xDCCA0004,
...
}
The hex value for each enum is deciphered as follows:
/// DCC X XXXX
/// --- - ----
/// | | |--> Notification ID (0x0000 to 0xFFFF)
/// | |-----> Notification Type (0x0 to 0xA)
/// |--------> Sub-system ID (0xDCC)
What is the best way to assign values to the enum's, such that adding enum's later won't mean reassigning all the values. The only Sub-system ID, and Notification Type are chosen, the Notification ID should be automatically assigned.
For example, it might get annoying if there were thousands of enum's and I had to number them by hand or renumber them if adding an enum in the middle.
Thanks.
If you asked me, you shouldn't be encoding this data in your enum values. It would be better to apply attributes the them instead where you could get this information. Let the actual value of the enum represent the NotificationId to get the automatically assigned values.
[AttributeUsage(AttributeTargets.Field, AllowMultiple=false)]
public class SubsystemIdAttribute : Attribute
{
public SubsystemIdAttribute(ushort value)
{
this.Value = (ushort)(value & 0xFFF);
}
public ushort Value { get; private set; }
}
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class NotificationTypeAttribute : Attribute
{
public NotificationTypeAttribute(byte value)
{
this.Value = (byte)(value & 0xF);
}
public byte Value { get; private set; }
}
public enum EventId
{
[SubsystemId(0xDCC)] [NotificationType(0xA)] SAMPLE_EVENT_1,
[SubsystemId(0xDCC)] [NotificationType(0xB)] SAMPLE_EVENT_2,
[SubsystemId(0xDCC)] [NotificationType(0xA)] SAMPLE_EVENT_3,
[SubsystemId(0xDCC)] [NotificationType(0x0)] SAMPLE_EVENT_4,
[SubsystemId(0xDCC)] [NotificationType(0xA)] SAMPLE_EVENT_5,
}
public static class EventIdExtensions
{
public static ushort GetSubsystemId(this EventId eventId)
{
return GetAttributeValue(eventId, (SubsystemIdAttribute a) => a.Value);
}
public static byte GetNotificationType(this EventId eventId)
{
return GetAttributeValue(eventId, (NotificationTypeAttribute a) => a.Value);
}
private static TValue GetAttributeValue<TAttribute, TValue>(EventId eventId, Func<TAttribute, TValue> selector)
where TAttribute : Attribute
{
return typeof(EventId).GetField(eventId.ToString())
.GetCustomAttributes(false)
.OfType<TAttribute>()
.Select(selector)
.Single();
}
}
To get the values of the attributes, call the appropriate extension methods.
var eventId = EventId.SAMPLE_EVENT_3;
var subsystemId = eventId.GetSubsystemId(); // 0xDCC
var notificationType = eventId.GetNotificationType(); // 0xA
Enums only auto-increment by 1, so you'd have to keep them in order by sub-system, then notification type, then notification id, and only when there are gaps would you assign. So to keep order proper, your above enum would look like this:
public enum EventID : uint
{
SAMPLE_EVENT_1 = 0xDCCA0000,
SAMPLE_EVENT_3 = 0xDCCA0002,
SAMPLE_EVENT_5 = 0xDCCA0004,
SAMPLE_EVENT_2 = 0xDCCB0001,
SAMPLE_EVENT_4 = 0xDCC00003,
}
I guess that purpose of your enums is to give a names to the events with specific codes. The question is what is the canonical source of association between names and codes. If it is you (or your code) I don't see any reason to renumber. If it comes from some external source (e.g. documentation) try to deploy some code generation (e.g. T4 templates).
If you are happy with Jeff's answer this is imho much more cleaner design
public class EventId
{
public static readonly SAMPLE_EVENT_1 = new EventId(0xDCC, 0xA);
public static readonly SAMPLE_EVENT_2 = new EventId(0xDCC, 0xA);
public static readonly SAMPLE_EVENT_3 = new EventId(0xDCC, 0xA);
public static readonly SAMPLE_EVENT_4 = new EventId(0xDCC, 0xA);
public readonly ushort SubSystemId;
public readonly byte NotificationType;
public readonly ushort NotificationId;
private static ushort notificationCounter = 0;
private EventId(ushort subSystemId, byte notificationType)
{
this.SubSystemId = subSystemId;
this.NotificationType= notificationType;
this.NotificationId = notificationCounter++;
}
}
But of course you creating dependency between compiler and NotificationId which you probably don't like.
Related
I am creating an gRPC service and we decided to choose the code first approach with protobuf-net.
Now I am running into a scenario where we have a couple of classes that need to be wrapped.
We do not want to define KnownTypes in the MyMessage class (just a sample name to illustrate the problem).
So I am trying to use the Any type which currently gives me some struggle with packing.
The sample code has the MyMessage which defines some header values and has to possiblity to deliver any type as payload.
[ProtoContract]
public class MyMessage
{
[ProtoMember(1)] public int HeaderValue1 { get; set; }
[ProtoMember(2)] public string HeaderValue2 { get; set; }
[ProtoMember(3)] public Google.Protobuf.WellknownTypes.Any Payload { get; set; }
}
[ProtoContract]
public class Payload1
{
[ProtoMember(1)] public bool Data1 { get; set; }
[ProtoMember(2)] public string Data2 { get; set; }
}
[ProtoContract]
public class Payload2
{
[ProtoMember(1)] public string Data1 { get; set; }
[ProtoMember(2)] public string Data2 { get; set; }
}
Somewhere in the code I construct my message with a payload ...
Payload2 payload = new Payload2 {
Data1 = "abc",
Data2 = "def"
};
MyMessage msg = new MyMessage
{
HeaderValue1 = 123,
HeaderValue2 = "iAmHeaderValue2",
Payload = Google.Protobuf.WellknownTypes.Any.Pack(payload)
};
Which doesn't work because Payload1 and Payload2 need to implement Google.Protobuf.IMessage.
Since I can't figure out how and do not find a lot information how to do it at all I am wondering if I am going a wrong path.
How is it intedend to use Any in protobuf-net?
Is there a simple (yet compatible) way to pack a C# code first class into Google.Protobuf.WellknownTypes.Any?
Do I really need to implement Google.Protobuf.IMessage?
Firstly, since you say "where we have a couple of classes that need to be wrapped" (emphasis mine), I wonder if what you actually want here is oneof rather than Any. protobuf-net has support for the oneof concept, although it isn't obvious from a code-first perspective. But imagine we had (in a contract-first sense):
syntax = "proto3";
message SomeType {
oneof Content {
Foo foo = 1;
Bar bar = 2;
Blap blap = 3;
}
}
message Foo {}
message Bar {}
message Blap {}
This would be implemented (via the protobuf-net schema tools) as:
private global::ProtoBuf.DiscriminatedUnionObject __pbn__Content;
[global::ProtoBuf.ProtoMember(1, Name = #"foo")]
public Foo Foo
{
get => __pbn__Content.Is(1) ? ((Foo)__pbn__Content.Object) : default;
set => __pbn__Content = new global::ProtoBuf.DiscriminatedUnionObject(1, value);
}
public bool ShouldSerializeFoo() => __pbn__Content.Is(1);
public void ResetFoo() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__Content, 1);
[global::ProtoBuf.ProtoMember(2, Name = #"bar")]
public Bar Bar
{
get => __pbn__Content.Is(2) ? ((Bar)__pbn__Content.Object) : default;
set => __pbn__Content = new global::ProtoBuf.DiscriminatedUnionObject(2, value);
}
public bool ShouldSerializeBar() => __pbn__Content.Is(2);
public void ResetBar() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__Content, 2);
[global::ProtoBuf.ProtoMember(3, Name = #"blap")]
public Blap Blap
{
get => __pbn__Content.Is(3) ? ((Blap)__pbn__Content.Object) : default;
set => __pbn__Content = new global::ProtoBuf.DiscriminatedUnionObject(3, value);
}
public bool ShouldSerializeBlap() => __pbn__Content.Is(3);
public void ResetBlap() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__Content, 3);
optionally with an enum to help:
public ContentOneofCase ContentCase => (ContentOneofCase)__pbn__Content.Discriminator;
public enum ContentOneofCase
{
None = 0,
Foo = 1,
Bar = 2,
Blap = 3,
}
This approach may be easier and preferable to Any.
On Any:
Short version: protobuf-net has not, to date, had any particular request to implement Any. It probably isn't a huge amount of work - simply: it hasn't yet happened. It looks like you're referencing both protobuf-net and the Google libs here, and using the Google implementation of Any. That's fine, but protobuf-net isn't going to use it at all - it doesn't know about the Google APIs in this context, so: implementing IMessage won't actually help you.
I'd be more than happy to look at Any with you, from the protobuf-net side. Ultimately, time/availability is always the limiting factor, so I prioritise features that are seeing demand. I think you may actually be the first person asking me about Any in protobuf-net.
My Any implementation.
[ProtoContract(Name = "type.googleapis.com/google.protobuf.Any")]
public class Any
{
/// <summary>Pack <paramref name="value"/></summary>
public static Any Pack(object? value)
{
// Handle null
if (value == null) return new Any { TypeUrl = null!, Value = Array.Empty<byte>() };
// Get type
System.Type type = value.GetType();
// Write here
MemoryStream ms = new MemoryStream();
// Serialize
RuntimeTypeModel.Default.Serialize(ms, value);
// Create any
Any any = new Any
{
TypeUrl = $"{type.Assembly.GetName().Name}/{type.FullName}",
Value = ms.ToArray()
};
// Return
return any;
}
/// <summary>Unpack any record</summary>
public object? Unpack()
{
// Handle null
if (TypeUrl == null || Value == null || Value.Length == 0) return null;
// Find '/'
int slashIx = TypeUrl.IndexOf('/');
// Convert to C# type name
string typename = slashIx >= 0 ? $"{TypeUrl.Substring(slashIx + 1)}, {TypeUrl.Substring(0, slashIx)}" : TypeUrl;
// Get type (Note security issue here!)
System.Type type = System.Type.GetType(typename, true)!;
// Deserialize
object value = RuntimeTypeModel.Default.Deserialize(type, Value.AsMemory());
// Return
return value;
}
/// <summary>Test type</summary>
public bool Is(System.Type type) => $"{type.Assembly.GetName().Name}/{type.FullName}" == TypeUrl;
/// <summary>Type url (using C# type names)</summary>
[ProtoMember(1)]
public string TypeUrl = null!;
/// <summary>Data serialization</summary>
[ProtoMember(2)]
public byte[] Value = null!;
/// <summary></summary>
public static implicit operator Container(Any value) => new Container(value.Unpack()! );
/// <summary></summary>
public static implicit operator Any(Container value) => Any.Pack(value.Value);
/// <summary></summary>
public struct Container
{
/// <summary></summary>
public object? Value;
/// <summary></summary>
public Container()
{
this.Value = null;
}
/// <summary></summary>
public Container(object? value)
{
this.Value = value;
}
}
}
'System.Object' can be used as a field or property in a surrounding Container record:
[DataContract]
public class Container
{
/// <summary></summary>
[DataMember(Order = 1, Name = nameof(Value))]
public Any.Container Any { get => new Any.Container(Value); set => Value = value.Value; }
/// <summary>Object</summary>
public object? Value;
}
Serialization
RuntimeTypeModel.Default.Add(typeof(Any.Container), false).SetSurrogate(typeof(Any));
var ms = new MemoryStream();
RuntimeTypeModel.Default.Serialize(ms, new Container { Value = "Hello world" });
Container dummy = RuntimeTypeModel.Default.Deserialize(typeof(Container), ms.ToArray().AsMemory()) as Container;
I have a base class like this:
public class Marker {
public int[] GetChildMarks() {
//Somehow Get Child int members
}
public int ShallNotBeInArray;
}
public class MyMarker : Marker {
public int Size, Number;
}
public class TedsMarker : Marker {
public int Power;
}
//... somewhere in main
var m = new MyMarker(){Size=3, Number = 666};
var arr = m.GetChildMarks(); // [Size, Number, Any other ints in MyMarker ]
var t = new TedsMarker() {Power=999};
var arr2 = t.GetChildMarks(); // [Power, Any other ints in TedsMarker]
So is it possible and how one can do such a thing in C# using reflection?
It can be achieved like this:
public class Marker
{
public int[] GetChildMarks()
{
return new int[]
{
(int)GetType().GetField("Size").GetValue(this),
(int)GetType().GetField("Number").GetValue(this)
};
}
public int ShallNotBeInArray;
}
This assumes that the child class has these two fields and they are of type int.
Since there is no enforcement (and actually there can't be because these are fields, not properties), this approach is a good candidate to produce runtime errors.
EDIT:
The values of all the fields can be retrieved like this:
public class Marker
{
public int[] GetChildMarks()
{
List<int> allIntegerFields = new List<int>();
// DeclaredOnly: only get fields declared by this type, not the ones declared by base classes
// Public | Instance: Only get non-static, public fields
foreach(FieldInfo fieldInfo in GetType().GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance))
{
if(fieldInfo.FieldType == typeof(int))
{
allIntegerFields.Add((int)fieldInfo.GetValue(this));
}
}
return allIntegerFields.ToArray();
}
public int ShallNotBeInArray;
}
Assume we want to define a class that contains all constants that we will use in our solution. And this class doesn't have any methods.
public class _const
{
/// Group1: 'DataBase'. We naming "db" group
public const Server_Name = 'ServerName';
public const DB_Name = 'DBName';
public const DB_User = 'UserName';
public const DB_Password = 'Password';
/// Group2: 'Default Variable'. We naming "default" group
public const Title= 'DefaultTitle';
public const KeyWord = 'DefaultKeyWord';
/// Group3: 'Status' constans.We naming "status" group
public const Approved = 'Approved';
public const Rejected = 'Rejected';
public const Suspended = 'Suspended';
/// And so on...
/// ...
/// ...
/// ...
}
We know, these constants will be used anywhere in the solution simply with below instruction. And don't need to create an instance of the class.
/// For example:
string x = _const.Approved;
My question is: for easier use and more code readability, is there any way for grouping related constant together? And access to each group with own prefix name (like namespace)?
Something similar to the following code.
/// For example:
string x = _const.db.DB_Name;
string y = _const.default.KeyWord;
string z = _const.status.Approved;
I think a mix of Enum and Dictionary could help you since, though with a little more verbose call, it assures consistency throughout your code:
public enum Db
{
Server_Name,
DB_Name,
DB_User,
DB_Password,
}
public enum Default
{
Title,
Keyword
}
public enum Status
{
Approved,
Rejected,
Suspened
}
public static class _const
{
public static Dictionary<Db, string> db = new Dictionary<Db, string>()
{
{Db.Server_Name, "ServerName"},
{Db.DB_Name, "DBName"},
{Db.DB_User, "UserName"},
{Db.DB_Password, "Password"}
};
public static Dictionary<Default, string> defaults = new Dictionary<Default, string>()
{
{Default.Title, "DefaultTitle"},
{Default.Keyword, "DefaultKeyWord"}
};
public static Dictionary<Status, string> status = new Dictionary<Status, string>()
{
{Status.Approved, "Approved"},
{Status.Rejected, "Rejected"},
{Status.Suspened, "Suspended"}
};
}
that you can use in your code like:
string string_x = _const.db[Db.Server_Name];
string string_y = _const.defaults[Default.Keyword];
string string_z = _const.status[Status.Suspened];
You can make your class static and define another nested static class like this:
public static class Constants
{
public static class Group1
{
internal const string String1 = "String1";
}
}
internal class Program
{
internal static void Main()
{
Console.WriteLine(Constants.Group1.String1);
}
}
Basically I want to define an enum with decimal values but this is not possible. An alternative is:
public static class EstadoRestriccion
{
public const decimal Valur1 = 0;
public const decimal Value2 = 0.5M;
public const decimal Value3 = 1;
};
But I need add these constants in a combobox where the options to display should be the name of constants and SelectedItem should return the value (0, 0.5M, 1) or some like these. I know that it is possible but it is ugly.
With an enum I can do this easly: comboBox.DataSource = Enum.GetValues(typeof(MyEnum));
What is the best way to simulate an enum with my requirements?
A dictionary may be a good choice.
Dictionary<string,decimal> could be a good candidate - letting you name the values.
var values = new Dictionary<string,decimal>();
values.Add("Value1", 0m);
values.Add("Value2", 0.5m);
values.Add("Value3", 1m);
This can be wrapped in a class so you only expose a getter by index, instead of the whole Dictionary<TKey,TValue> interface.
THERE IS NO WAY simply. enum accepts only integer values. The code snippet you put is good.
There is a small difference between const decimal and static readonly decimal. The first is direct evaluation; the compiler replaces the name with its value. In contrast, readonly inforces the code to refer to the field each time and bring the value from it. You can observe why readonly is being used with reference types while const cannot be (expect for a string).
You could change your class slightly:
public class EstadoRestriccion
{
public static readonly EstadoRestriccion Value1 = new EstadoRestriccion("Value1", 0);
public static readonly EstadoRestriccion Value2 = new EstadoRestriccion("Value2", 0.5M);
public static readonly EstadoRestriccion Value3 = new EstadoRestriccion("Value3", 1);
private static readonly EstadoRestriccion[] values = new EstadoRestriccion[] { Value1, Value2, Value3 };
private string name;
private decimal value;
private EstadoRestriccion(string name, decimal value)
{
this.name = name;
this.value = value;
}
public static EstadoRestriccion[] GetValues()
{
return values;
}
public override string ToString()
{
return this.name;
}
};
And some decimal conversion and/or change value to be a public property.
How about a static readonly array of decimal?
public static readonly decimal[] myValues = new[] {0, 0.5M, 1};
i need an enum or something similiar to do something like this:
public enum MyStringEnum {
[StringValue("Foo A")] Foo = "A",
[StringValue("Foo B")] Foo = "B" }
is this possible? my example, i return back a dataset with a value represented as either A,B,C,D,E .. i need a solution to return this as a string representation?
i guess the obvious would be to create an extension method or something which just had a switch statement in and return a string .. any other cleaner solutions?
regards,
dave
Here is something we use for our MVC applications to retrieve a display name for our enums. It uses a custom attribute and an extension method to retrieve the enum display name.
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class EnumDisplayNameAttribute : Attribute
{
public EnumDisplayNameAttribute(string displayName)
{
DisplayName = displayName;
}
public string DisplayName { get; set; }
}
public static string GetDisplayName(this Enum enumType)
{
var displayNameAttribute = enumType.GetType()
.GetField(enumType.ToString())
.GetCustomAttributes(typeof(EnumDisplayNameAttribute), false)
.FirstOrDefault() as EnumDisplayNameAttribute;
return displayNameAttribute != null ? displayNameAttribute.DisplayName : Enum.GetName(enumType.GetType(), enumType);
}
Usage on the enum:
public enum Foo
{
[EnumDisplayName("Foo Bar")]
Bar = 0
}
Getting back the display name:
var f = Foo.Bar;
var name = f.GetDisplayName();
Would it be an option not to use enum and use structs instead?
struct FooEnum
{
private int value;
private string name;
private FooEnum(int value, string name)
{
this.name = name;
this.value = value;
}
public static readonly FooEnum A = new FooEnum(0, "Foo A");
public static readonly FooEnum B = new FooEnum(1, "Foo B");
public static readonly FooEnum C = new FooEnum(2, "Foo C");
public static readonly FooEnum D = new FooEnum(3, "Foo D");
public override string ToString()
{
return this.name;
}
//TODO explicit conversion to int etc.
}
You could then use FooEnum like an enum with an own ToString() overload:
FooEnum foo = FooEnum.A;
string s = foo.ToString(); //"Foo A"
If you want to do something like this:
MyStringEnum value = MyStringEnum.A;
string description = value.GetDescription();
// description == "Foo A"
Setup your enum like this:
public enum MyStringEnum
{
[Description("Foo A")]
A,
[Description("Foo B")]
B
}
And use a utility/extension method that reads the attribute:
public static string GetDescription(this MyStringEnum enumerationValue)
{
Type type = enumerationValue.GetType();
string name = enumerationValue.ToString();
//Tries to find a DescriptionAttribute for a potential friendly name for the enum
MemberInfo[] member = type.GetMember(name);
if (member != null && member.Length > 0)
{
object[] attributes = member[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
{
//Pull out the description value
return ((DescriptionAttribute)attributes[0]).Description;
}
}
return name;
}
I've seen this done where I would put
MyStringEnum.Foo.ToString();
In this case it would give "A"
The cleanest solution for this problem is to create a custom attribute that will store the string value you want for the enum constant. I've used that strategy in the past and it worked out fairly well. Here's a blog post detailing the work involved:
Enum With String Values In C# - Stefan Sedich's Blog
Of course this is only necessary if you need some kind of meaningful text. If the name of the enum constant works for you...then you can simply call ToString().