5

Background

I have a convenience class which is essentially a dictionary that holds a tag/value pair list for submission to an API.

At its very basic level all I did is this:

enum Tag
{
    Name, Date, //There are many more
}

class RequestDictionary : Dictionary<Tag, string>
{
}

This maps well to the needs of the API, since the whole thing gets sent and parsed as a string. However, it's not so great for the caller; for example, the caller has to know how to format the Date properly.

To address this, I started adding tag-specific properties that are type safe. I have them sequestered in separate interface so they don't get confused with the normal Dictionary properties.

enum Tag
{
    Name, Date
}

interface ITags
{
    string Name { get; set; }
    DateTime Date { get; set; }
}

class RequestDictionary : Dictionary<Tag, string>, ITags
{
    public ITags Tags { get { return this; } }

    string ITags.Name 
    { 
        get { return this[Tag.Name]; }
        set { this[Tag.Name] = value; }
    }

    DateTime ITags.Date 
    { 
        get { return DateTime.ParseExact(this[Tag.Date], API_DATE_FORMAT, CultureInfo.InvariantCulture); }
        set { this[Tag.Name] = value.ToString(API_DATE_FORMAT); }
    }
}

After introducing this interface, the caller has a choice of

dict[Tag.Date] = DateTime.Now.ToString(API_DATE_FORMAT);

or

dict.Tags.Date = DateTime.Now;

The latter is less work for the caller, especially since API_DATE_FORMAT is actually private.

The issue

I want the caller to be able to use object initializer syntax:

var dict = new RequestDictionary
{
    Tags.Date = DateTime.Now
}

...but this does not compile (the error is "Invalid initializer member declarator").

So it seems that the caller will need to use

var dict = new RequestDictionary
{
    { Tags.Date, DateTime.Now.ToString(API_DATE_FORMAT) }    
};

...which is obviously not as well encapsulated or convenient.

Is there any way to initialize an object using object initializer syntax when the property you wish to access is exposed via an interface that is not included in the default class interface?

John Wu
  • 50,556
  • 8
  • 44
  • 80
  • There isn't any way to use object initializer syntax with either (a) a value on the left-hand of the equals (only identifiers are allowed) or (b) private members – Ben Voigt Mar 23 '18 at 22:32
  • 1
    Possible duplicate of [Assignment to readonly property in initializer list](https://stackoverflow.com/questions/15784483/assignment-to-readonly-property-in-initializer-list) – mjwills Mar 23 '18 at 23:08
  • 1
    I am not certain this is a duplicate. Strictly speaking it is a converse. I posted a [question on meta](https://meta.stackoverflow.com/questions/365031/should-i-accept-the-duplicate-if-it-is-really-a-converse) to decide. – John Wu Mar 24 '18 at 01:05

1 Answers1

3

The following code may serve your needs:

var dict = new RequestDictionary
{
    Tags = { Date = DateTime.Now }
};

The key is to use Tags = { Date (i.e. go one level down at a time) rather than Tags.Date =.

I acknowledge that the code looks like it won't work, since it looks like you are assigning to a read-only property (i.e. Tags). But it does work (due to how object initializers work).

mjwills
  • 23,389
  • 6
  • 40
  • 63
  • Impressive. Thanks for sharing this; I didn't know about that syntax. Out of curiosity, is this documented anywhere on MSDN? I can't find it under Object Initializers. – Douglas Mar 23 '18 at 23:00
  • https://stackoverflow.com/a/15784586/34092 has the relevant excerpt from the C# 4 spec @Douglas – mjwills Mar 24 '18 at 00:28