1

I am a C++ programmer moving to C# (so complete newb really). So far its a pretty easy transition :)

I am porting some code (well, re-writing it) from C++ to C#. I am stuck with lots of possibilities on how to port the following C++ STL structures. Here is a C++ code snippet of my C++ structure layout (I have not bothered showing the enums to save on clutter, but I can add if required):

struct DeviceConnection_t
{
    DeviceType_e  device;
    DeviceState_e state;
    bool          isPass;

    DeviceConnection_t() :
        device(DEV_TYPE_UNKNOWN),
        state(DEV_STATE_DISCONNECTED),
        isPass(false)
    {}
};

struct Given_t
{
    std::string text;
    std::vector<DeviceConnection_t> deviceConnections;
};

struct Action_t
{
    ActionEventType_e  type;
    uint32_t           repeat_interval;
    uint32_t           repeat_duration;
    DeviceType_e       device;
    bool               isDone;

    Action_t() :
        type(AE_TYPE_UNKNOWN),
        repeat_interval(0),
        repeat_duration(0),
        device(DEV_TYPE_UNKNOWN),
        isDone(false)
    {}
};

struct When_t
{
    std::string text;
    std::multimap<uint32_t, Action_t> actions; // time, action
};

So here I have a vector of DeviceConnection_t, which I have read here: c-sharp-equivalent-of-c-vector-with-contiguous-memory can just be made into a C# List<DeviceConnection_t>. That seems to work, so far so good.

Next is my multimap<int, Action_t> where the int is a time value where duplicate entries are expected/allowed. I read here: multimap-in-net that there is no equivalent in C#, but there are various implementations out there.

So I could use one of these, but other questions I read like: order-list-by-date-and-time-in-string-format got me thinking there might be a better way to achieve what I want.

What I really want is: 1.A list of Action_t in time order - where time could be an element of Action_t (I removed it as a element in my c++ because it became my multi-map key). I also need to be able to search through the collection to find time values. 2. Some sort of default constructor to populate the default values of a newly instantiated struct, but I can't see how this is done either.

I really like the look of the Dictionary C# class, but I don't think that fits any of my requirements at the moment (might be wrong here).

So my two questions are:

  1. What is the best way to create a time ordered collection of objects?
  2. How can I assign default values to a new instance of a structure? (in the same way a default constructor does in C++)?
Community
  • 1
  • 1
code_fodder
  • 15,263
  • 17
  • 90
  • 167

2 Answers2

1

By using struct, it is impossible to enforce initial values. No explicit default constructor can be provided and in case of default construction, all values will be initialized with their default value. It is only possible to provide additional constructors, where fields can be initialized. In the example, if AE_TYPE_UNKNOWN and DEV_TYPE_UNKNOWN would be 0, then default initialization would actually be equivalent to your values.

struct Action_t
{
    // example constructor, but there will always be a default constructor
    public Action_t(ActionEventType_e type, DeviceType_e device)
    {
        this.type = type;
        this.device = device;
        this.isDone = false;
        this.repeat_interval = 0;
        this.repeat_duration = 0;
    }

    public ActionEventType_e type;
    public UInt32 repeat_interval;
    public UInt32 repeat_duration;
    public DeviceType_e device;
    public bool isDone;
}

If you need to enforce initialization with values that differ from the default, then you need to create a class, where explicit initialization is possible.

class Action_t
{
    public ActionEventType_e type = ActionEventType_e.AE_TYPE_UNKNOWN;
    public UInt32 repeat_interval = 0;
    public UInt32 repeat_duration = 0;
    public DeviceType_e device = DeviceType_e.DEV_TYPE_UNKNOWN;
    public bool isDone = false;
}

However, for more flexibility I'd advice to use public properties, either as auto properties or as public properties with private backing field. Depending on your choice and used language standard version, you have different options how to write the properties and the initialization:

class Action_t
{
    public Action_t()
    {
        repeat_interval = 0;
    }
    public UInt32 repeat_interval { get; set; }


    private UInt32 _repeat_duration = 0;
    public UInt32 repeat_duration
    {
        get { return _repeat_duration; }
        set { _repeat_duration = value; }
    }

    public bool isDone { get; set; } = false; // valid for C# 6
}

You should read into the differences between struct and class in C#, since there are some mayor differences that you may not expect as a C++ programmer, where struct is basically a public-default class. Then decide, if struct is suited for your case.


The best equivalent to a sorted multimap would probably be a SortedDictionary<key, ICollection<values>> with an add method that handles new keys / adding to existing keys.

IDictionary<DateTime, ICollection<Action_t>> actions = new SortedDictionary<DateTime, ICollection<Action_t>>();
void AddAction(DateTime key, Action_t action)
{
    ICollection<Action_t> values;
    if (!actions.TryGetValue(key, out values))
    {
        values = new List<Action_t>();
        actions[key] = values;
    }
    values.Add(action);
}
grek40
  • 13,113
  • 1
  • 24
  • 50
  • good explanation for the struct/class and nice little implementation of "multimap", thanks + 1 : ) – code_fodder Oct 04 '16 at 07:28
  • @code_fodder Another note on the multimap: C++ multimap stores values with same key in order of insertion and allows duplicate values per key. If you don't want this, you can use a different type for the inner values collection, for example a `HashSet` for unordered unique values per key. – grek40 Oct 04 '16 at 07:44
  • Yeah, that's an interesting point actually - thank you. My current scenario does not really care about the values being duplicated - i.e. its perfectly valid to have the same action occur at the same time : ) – code_fodder Oct 04 '16 at 07:51
  • I don't think your struct examples work :(. In your first example I get the error "cannot have instance field initializers in structs". And your second example gives the error: "Structs cannot contain explicit parameterless constructors". Are you sure you examples are correct? thanks : ) – code_fodder Oct 04 '16 at 10:44
  • Aw, screw it... I coded it in VS and it didn't show any error on the fly, but I didn't hit the compile button on the final code. I'll update the part about initialization. – grek40 Oct 04 '16 at 10:49
  • Ahh....cool - I just renamed all my `struct` to `class` and it sprung to life :) ... little unsure what the point of C# struct is, but I'll have to go a read up on that. Thanks : ) – code_fodder Oct 04 '16 at 12:07
  • @code_fodder thats why I explicitely warned, that C# `struct` is very different from C++ `struct` and you should read something about it (guess I should have followed my own advice before posting the answer). – grek40 Oct 04 '16 at 12:14
0

Unfortunately C# doesn't seem to have a sorted List. The Dictionary is fine if you have Key, Value pairs.

1) If its just a collection (List) you can take a look at the discussion here: Is there a SortedList<T> class in .NET?. Otherwise you can manually sort the collection(I named it sort in my example) like:

 actions.Sort((x, y) => x.time.CompareTo(y.time));

In this your time object should be a IComparable or a primitive, but you can replace "x.time.CompareTo" to any other sorting method. (Based on: List<> OrderBy Alphabetical Order).

If you use a list you can just search the collection with linq:

 actions.First(x=>x.time.certainValue == DesiredValue);

But there are many functions to search through the tree. There are some displayed: https://msdn.microsoft.com/en-us/library/system.linq.enumerable_methods(v=vs.110).aspx

2) There are multiple ways to do this. First off, the default constructor:

 Action_t() {
    type=AE_TYPE_UNKNOWN;
    repeat_interval=0;
    repeat_duration=0;
    device= DEV_TYPE_UNKNOWN);
    isDone = false;
}

This works like any other code. But if all values are public Properties (Also a variable: https://msdn.microsoft.com/nl-nl/library/x9fsa0sw.aspx) then you can remove the constructor (or have a public one that you can access) and create new instances with:

new Action_t {
    type=AE_TYPE_UNKNOWN,
    repeat_interval=0,
    repeat_duration=0,
    device= DEV_TYPE_UNKNOWN),
    isDone = false
}

The difference is where the variables are set. The default constructor is always safe.

I hope this answers your question!

Community
  • 1
  • 1
Flying Dutch Boy
  • 344
  • 3
  • 17
  • +1 for the list sorting ideas - also I thought I read that you can't use constructors like this, so thanks also for clarifying that - I will give that a go : ) – code_fodder Oct 04 '16 at 07:20