6

I want to make the int in IDictionary<int, Driver> a little less obscure about its meaning, so I thought maybe wrapping it would be a good idea. The outcome would be something like IDictionary<finishPosition, Driver>.

Preferably, finishPosition should be assignable like this finishPosition = 1.

I do not know if this is possible and how.

H H
  • 263,252
  • 30
  • 330
  • 514
Garth Marenghi
  • 1,997
  • 5
  • 20
  • 38

5 Answers5

6

You could use a struct with an implicit conversion.

public struct FinishPosition {
    public readonly int Position;

    public FinishPosition(int position) {
        this.Position = position;
    }

    public static implicit operator FinishPosition(int position) {
        return new FinishPosition(position);
    }
}


// ...

Dictionary<FinishPosition, Driver> dict = new Dictionary<FinishPosition, Driver>();
Driver bob = new Driver();

// The following two lines are effectively equivalent
dict[new FinishPosition(7)] = bob;
dict[7] = bob;

// So are these two
bob = dict[7];
bob = dict[new FinishPosition(7)]
configurator
  • 40,828
  • 14
  • 81
  • 115
  • This is clearly a better solution than mine. Less confusing by far. – Martin Doms Mar 27 '11 at 20:51
  • Seems correct and what was asked, but it's quite a bit of overhead... And for readability (and safety) I would omit the implicit conversion. – H H Mar 27 '11 at 21:38
  • @Henk: The entire point of this is the implicit conversion; it doesn't hurt readability as much as you would think when it only works one way. – configurator Mar 27 '11 at 21:44
  • @Config: Yes, it was asked for. But it does eat directly into the readability. A conflict in the question. – H H Mar 27 '11 at 21:46
  • @Henk: it's true that implicit casts actively hurt readability in some cases, but they also help in other cases. For example, if you have implicit casts in both ways it's sometimes very confusing. But for wrappers like this it's extremely convenient and doesn't reduce readability in any way. – configurator Mar 27 '11 at 21:49
  • Thanks both for clearing up some more of the issues. Using an int (like in `Dictionary`) could make people wonder what the int stands for - on the other hand, using a FinishPosition struct could do the same thing (is it an int... or what?). – Garth Marenghi Mar 28 '11 at 06:46
5

Absolutely, just use this using directive:

using FinishPosition = System.Int32;

at the top of your source file, and write the code just as you have in your example.

If you use the using directive like this, FinishPosition is an int and other files will see it as an int - there is no new type FinishPosition defined (thanks for configurator in the comments).

Martin Doms
  • 8,598
  • 11
  • 43
  • 60
  • Awesome! Do you know if there is an equivalent in VB.NET for this trick? – Maxim Gershkovich Mar 27 '11 at 20:20
  • Just tried it and it works in VB.NET too. Imports myValue = System.Int32 – Maxim Gershkovich Mar 27 '11 at 20:21
  • 2
    Erm... No it doesn't. The problem may be that `FinishPosition` _is_ an `Int32` and other files will see it as an `int`. But it doesn't "hide" it or anything like that... Why would it? – configurator Mar 27 '11 at 20:25
  • It "hides" access to Int32 within that particular source file. Try it for yourself. Use that using directive, then try to declare a FinishPosition variable and an int variable. You'll find the compiler will fail to find the type Int32 on the int declaration. – Martin Doms Mar 27 '11 at 20:28
  • Interesting, I just tried again, it seems it fails to find Int32 but it will find int. – Martin Doms Mar 27 '11 at 20:36
  • 2
    Haha ok that's only because I was missing `using System`. /facepalm :P I'll edit my answer. That'll teach me to write test code in Notepad++ instead of VS. – Martin Doms Mar 27 '11 at 20:38
  • @Martin: What version of the compiler are you using? That's really weird. It's definitely not correct according to the spec, and I've never seen such behaviour. – configurator Mar 27 '11 at 20:39
  • @Martin: Could you incorporate my previous comment into your answer? It's really incomplete without it. "If you use the using directive like this, `FinishPosition` is an `int` and other files will see it as an `int` - there is no new type `FinishPosition` defined." – configurator Mar 27 '11 at 20:41
  • Sure thing configurator. Thanks for the help. – Martin Doms Mar 27 '11 at 20:47
  • 2
    That's a potentially confusing way of doing it. It's still an int. Any documentation will say it's an it (it's simply called so Else in the source file. The information is completely gone when compiling and intellisense in different files shoul report it As int. The syntax is meant for disambiguation – Rune FS Mar 27 '11 at 20:48
  • Like @Rune says: this is completely local to a file and won't export to the calling code (where it would be most useful). – H H Mar 27 '11 at 21:35
1

You could use an enum:

enum Position { Finish, Some, Other }

IDictionary<Position, Driver> lookup = ...;

Driver d = lookup[Position.Finish];
H H
  • 263,252
  • 30
  • 330
  • 514
  • Maybe I understand you wrong (or the other way around) - But the finishPosition should be assignable with different kinds of integer, like finishPosition = 1, finishPosition = 2, finishPosition = 3, and assign each of these with a Driver object for that position - and storing these in the dictionary. the dictionary will be drivers and the positions they finished in. – Garth Marenghi Mar 27 '11 at 19:53
  • Best thing is to name those integers (Some, Other) but you can also assign an arbitrary int value. Maybe I got your naming wrong though, adapt. – H H Mar 27 '11 at 19:58
  • You can't assign arbitrary integers to an enum variable. Only `0` or an `enum` value, or with explicit casting. – configurator Mar 27 '11 at 20:26
  • @config: Yes on the explicit cast. – H H Mar 27 '11 at 21:33
  • Thanks for your answer, though I'm still trying to wrap my head around it :-) – Garth Marenghi Mar 28 '11 at 06:36
1

In my opinion, it is good to use AbstractDataType pattern anywhere where a general value is used with specific intents (this case has been made by many others with examples like Percentage, ReadonlyString, ConnectionString, etc.)

Note, I personally think that having implicit conversions to and from them sort of hollows the whole thing out (by having an implicit conversion, there is no more compiler guarantee that generic values are being used with specific intent at the right place).

Here is a sample that should inspire you to have it your way: you can still choose the level of convenience/verbosity you prefer. It show two approaches:

  • _byInt indexes a dictionary by PseudoInt
  • _byEnum indexes a dictionary by an Enum : int type

Notes

Without further ado:

#define IMPLICIT_CONVERSIONS
using System.Collections.Generic;

namespace NS
{
    public enum PositionEnum : int { Begin = 0, Normal = 1, End = 99 }

    public struct Pseudo<T> where T : struct
    {
        public T Value;
        public Pseudo(T value) { Value = value; }
#if IMPLICIT_CONVERSIONS
        public static implicit operator T(Pseudo<T> pi)  { return pi.Value; }
        public static implicit operator Pseudo<T>(T ni)  { return new Pseudo<T>(ni); }
#endif
    }

    static class Program
    {
        private static Pseudo<T> AsPseudo<T>(this T value) where T : struct
        {
            return new Pseudo<T>(value);
        }

        private static readonly IDictionary<Pseudo<int>, string> _byInt = 
            new Dictionary<Pseudo<int>, string>()
                {   { 0,  "aap" },
                    { 1,  "noot" },
                    { 99, "mies" },
                };

        private static readonly IDictionary<Pseudo<PositionEnum>, string> _byEnum = 
            new Dictionary<Pseudo<PositionEnum>, string>()
                {   { PositionEnum.Begin,  "aap" },
                    { PositionEnum.Normal, "noot" },
                    { PositionEnum.End,    "mies" },
                };

        public static void Main(string[] args)
        {
            string s;
            s = _byInt[0];
            s = _byEnum[PositionEnum.Normal];
        }
    }
}
Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
0

I just adapted the RichEnum generic baseclass in the question I linked before [1]

I'd say I prefer this method because it really seems the most flexible in the long run. The only downside I can think of, really, is that the RichEnum type cannot be a value type anymore.

Head over to see what I mean (includes a fully running sample again):

[1] Can we define implicit conversions of enums in c#?

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633