49

I have an unchanging dictionary that is exposed in a class.

Currently my code looks like

using System.Collections.Generic;
using System.Collections.ObjectModel;

public class FooClass
{
    private static ReadOnlyDictionary<string, byte> _validRevisions 
        = new ReadOnlyDictionary<string, byte>(
            new Dictionary<string, byte>() { 
                { "1.0", 0x00 },
                { "1.1", 0x01 },
                { "1.2", 0x02 },
                { "1.3", 0x03 }
            } );

    public static ReadOnlyDictionary<string, byte> ValidRevisions => _validRevisions;

    // other FooClass contents...
}

I've used the backing field _validRevisions as I could not figure out a better way of creating a shared constant dictionary property. Is there neater way to manage this or maybe I should just make the field public?

My main question being is there a shorter way to initialise the _validRevisions field itself? Creating a Dictionary inline to pass it into a Dictionary (that happens to be read only) seems a bit... of a code smell. Is it? Is there a better way?

EDIT: one more thing about the ROD I just noticed, there are no methods to support checking if it contains a given value... is there a reason for that related to it' read-only-ness?

Toby
  • 9,696
  • 16
  • 68
  • 132
  • 1
    You tagged C#4 but used a C#6 feature (the => operator), which one are you using? – Camilo Terevinto May 30 '17 at 12:45
  • @CamiloTerevinto Whoops, wasn't aware that was C#6! Will change now – Toby May 30 '17 at 12:46
  • Dictionary implements IReadOnlyDictionary so your _validRevisions can be an instance of Dictionary, whereas ValidRevisions can just expose IReadOnlyDictionary part. – Ivan G. May 30 '17 at 12:49
  • @Ivan that would allow the user to cast and modify it. Not mentioned as a constraint in the question though. – CodeCaster May 30 '17 at 12:50
  • Yep, the idea of this is that the exposed dictionary would be unmodifiable – Toby May 30 '17 at 12:52
  • 3
    Note that you can use an automatically implemented property instead: `public static ReadOnlyDictionary ValidRevisions { get; } = new ...` – Jon Skeet May 30 '17 at 12:52
  • _"there are no methods in a `ReadOnlyDictionary` to support searching it by value"_ but a dictionary also dont has this method – Tim Schmelter May 30 '17 at 12:55
  • @TimSchmelter normal dictionaries have the `ContainsValue()` method -- not quite the same I'll clarify in question – Toby May 30 '17 at 12:57
  • @Toby: but how is this question related to the original question(how to initialise ...)? Don't ask btw-questions, ask a separate question – Tim Schmelter May 30 '17 at 13:02
  • @TimSchmelter Sorry, my train of thought got away from me! :D – Toby May 30 '17 at 13:03
  • If performance is a concern, see this answer for ImmutableDictionary; which has the same scenario: https://stackoverflow.com/a/28448092/361842 – JohnLBevan Oct 31 '18 at 15:24

3 Answers3

40

If you don't mind having an IReadOnlyDictionary instead of a ReadOnlyDictionary, you could use this, since Dictionary implements IReadOnlyDictionary:

private static IReadOnlyDictionary<string, byte> _validRevisions
    = new Dictionary<string, byte>
       {
           { "1.0", 0x00 },
           { "1.1", 0x01 },
           { "1.2", 0x02 },
           { "1.3", 0x03 }
        };

public static IReadOnlyDictionary<string, byte> ValidRevisions => _validRevisions;
Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
  • 32
    That would allow the user of this class to cast `ValidRevisions` back to a `Dictionary` and modify its contents. That is not mentioned as a constraint in the question though. – CodeCaster May 30 '17 at 12:55
  • 1
    @CodeCaster just made a console to try it out, you are right. Didn't think it would be possible – Camilo Terevinto May 30 '17 at 13:02
  • @CodeCaster, as mentioned in the comments above, I would indeed like this to be immutable (is that not the same thing as what you're saying?) – Toby May 30 '17 at 13:04
  • 1
    @Toby yes, my example makes it mutable, so it will not suit your case – Camilo Terevinto May 30 '17 at 13:05
  • 4
    @CodeCaster user of this class can use reflection to change private readonly field of `ReadonlyDictionary` which holds decorated dictionary instance. I always wonder why casting interface to some data type is considered to be legal behavior of client – Sergey Berezovskiy May 30 '17 at 13:09
  • 1
    @Sergey heh, that's true. – CodeCaster May 30 '17 at 13:12
29

Update: In .NET 7 we got the AsReadOnly extension method for IDictionary<TKey, TValue>:

IDictionary<string, string> dict = new Dictionary<string, string>();
ReadOnlyDictionary<string, string> readonlyDict = dict.AsReadOnly();

Original answer:

The ReadOnlyDictionary<TKey, TValue> is just a wrapper around a normal dictionary and there is only one constructor to initialize it which takes another Dictionary instance.

So no, there is no shorter way. But i'd use the static constructor to initialize complex static objects:

private static ReadOnlyDictionary<string, byte> _validRevisions;

static FooClass()
{
    IDictionary<string, byte> dict = new Dictionary<string, byte>() { 
            { "1.0", 0x00 },
            { "1.1", 0x01 },
            { "1.2", 0x02 },
            { "1.3", 0x03 }
        };
    _validRevisions = new ReadOnlyDictionary<string, byte>(dict);
}
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • 11
    Am I completely crazy or was there some extension method like "AsReadOnly" that converted the Dictionary to its readonly counterpart? – Camilo Terevinto May 30 '17 at 13:11
  • 4
    @CamiloTerevinto You are not insane, but `AsReadOnly` is an extension for collections, not for dictionaries, so it will work with lists or arrays. – VMAtm May 30 '17 at 14:32
  • 3
    `public static ReadOnlyDictionary AsReadOnly(this Dictionary source) => new ReadOnlyDictionary(source);` ← There you have it – Mikael Dúi Bolinder Feb 19 '19 at 16:06
  • 2
    This equally depends on what `T2` actually is, the runtime will comply with what you tell it, even if that's a little contradictory. For example, `ReadOnlyDictionary>` will still expose a mutable list as the `Value` for each `Key`. In practice, if you're building up such a dictionary internally and just wanting to expose it as readonly, then you'll have to re-implement each `Value` as its own `IEnumerable` – dyson Oct 29 '19 at 09:14
  • 1
    In .NET 7 there is now an `AsReadOnly` extension method on `IDictionary`. – glen-84 Dec 15 '22 at 20:11
-1

This is the shortest version I made

private static IReadOnlyDictionary<string, byte> _validRevisions => 
new Dictionary<string, byte>() { { "1.0", 0x00 }, { "1.1", 0x01 }, 
{ "1.2", 0x02 }, { "1.3", 0x03 } }
  • The question asks about initializing a `ReadOnlyDictionary`, not an `IReadOnlyDictionary`. The former is a concrete type and the later is an interface. – Theodor Zoulias Nov 05 '22 at 08:39