Setting jagged array values within a class - c#

I'm having trouble understanding this problem with creating a jagged array in C#. If you look at the code below, it compiles fine. But if I were to take the 2 lines that are assigning values to the array "places" and move them from within the method to within the class itself, the compiler starts complaining with a lot of strange errors.
At first I thought that its because of the usage of the 'new' keyword within a class (a class being just a definition whereas 'new' refers to an instance... you can't have an instance within a definition, can you?). But then I noticed the usage of the 'new' keyword in the initialization of "places" was OK even though it was initialized within the class. Please explain.
public class Place
{
string[][] places = new string[2][];
public void enumerate()
{
places[0] = new string[] { "Canada", "United States" };
places[1] = new string[] { "Calgary", "Edmonton", "Toronto" };
Console.WriteLine(places[0][1]);
}
}
EDIT: to be explicit, the error is produced when running
public class Place
{
string[][] places = new string[2][];
places[0] = new string[] { "Canada", "United States" };
places[1] = new string[] { "Calgary", "Edmonton", "Toronto" };
public void enumerate()
{
Console.WriteLine(places[0][1]);
}
}
errors received "array size cannot be specified in a variable declaration (try initializing with a "new" expression)" "invalid tokens =, {, in class" "namespace cannot directly contain members such as fields or methods"

Of course you can initialize an jagged array, but the syntax is slightly different:
public class Place
{
private string[][] places = new string[2][]
{
new string[] { "Canada", "United States" },
new string[] { "Calgary", "Edmonton", "Toronto" },
};
public void enumerate()
{
Console.WriteLine(places[0][1]);
}
}

Related

Initialize array of key-value tuples of which the value has a generic type

I need an array of key-value tuples of which the value has a generic type. I do not want to declare value as System.Object. Desired usage would be like this:
Pair[] pairs = new Pair[]
{
new Pair<int> { Name = "Age", Value = 44 },
new Pair<string> { Name = "FirstName", Value = "Frank" },
};
With Pair defined like this:
public class Pair<T>
{
public string Name { get; set; }
public T Value { get; set; }
}
This gives the following compile error when declaring and initializing pairs:
Using the generic type 'Pair<T>' requires 1 type arguments
If I would change the code like this:
Pair<int>[] pairs = new Pair<int>[]
{
new Pair<int> { Name = "Age", Value = 44 },
new Pair<string> { Name = "FirstName", Value = "Frank" },
};
It would obviously satisfy the Pair<int> but not the Pair<string>:
Cannot implicitly convert type 'Pair<string>' to 'Pair<int>'
If I would change the code like this:
Pair<object>[] pairs = new Pair<object>[]
{
new Pair<int> { Name = "Age", Value = 44 },
new Pair<string> { Name = "FirstName", Value = "Frank" },
};
Then the compile error is (twice):
Cannot implicitly convert type 'Pair<int>' to 'Pair<object>'
--- update
The reason that I do not want to declare Pair.Value as object is that I want to define special pairs as follows:
public class AgePair : Pair<int> { }
public class NamePair : Pair<string> { }
Although Pair<int> and Pair<string> share the classdefinition of Pair<T>, they are compiled to entirely different types at runtime. Thatfor, you cannot put them in an IEnumerable<T> or T[] Type. You simply cannot do that (directly).
If you let your Class Pair<T> inherit from a class Pair which returns the Value object as Type object, you can use a IEnumerable<Pair> or Pair[] and upon use of the object check the typeof(Value) respectively.
This is very unclean though and changes the implementation of the result based on its type instead of it‘s definition which would violate OOP principles and adds hidden complexity. This is exactly the reason why Key-Value storage is usually done as string-string or string-object with object being of a type that‘s defined in a complex (multiple Properties and Methods) class.

Can I pass arguments to a base constructor from a derived class's default constructor?

Suppose I have an abstract base class Deck:
public abstract class Deck
{
public List<Card> cards;
public Deck(string[] values, string[] suits)
{...}
...
}
and a derived class EuchreDeck:
public class EuchreDeck : Deck
{
string[] values = new string[] { "9", "10", "J", "Q", "K", "A" };
string[] suits = new string[] { "clubs", "spades", "hearts", "diamonds" };
public EuchreDeck() : base(values, suits) // Error.
{}
...
}
I want the ability to instantiate EuchreDeck and have the two string arrays passed to the base class, i.e. var gameDeck = new EuchreDeck();.
Currently I'm getting the error: "An object reference is required for the non-static field, method, or property EuchreDeck.values."
Is this possible, or will calling the derived default constructor always call the base default constructor?
Yes, you can do this if you make the arrays static:
public class EuchreDeck : Deck
{
private static readonly string[] values = new string[] { "9", "10", "J", "Q", "K", "A" };
private static readonly string[] suits = new string[] { "clubs", "spades", "hearts", "diamonds" };
public EuchreDeck() : base(values, suits)
{
}
}
The reason why you can't use it as you had with instance-level members is because it's not legal to do so. This comes from the C# specification 10.10.1 Constructor Initializers where it states:
An instance constructor initializer cannot access the instance being
created. Therefore it is a compile-time error to reference this in an
argument expression of the constructor initializer, as is it a
compile-time error for an argument expression to reference any
instance member through a simple-name.
By switching the arrays to be static, they are no longer accessed via the instance but rather by the EuchreDeck type.
That said, I might suggest you take a slight tweak on the design. Maybe use a factory to create these specialized decks for you rather than their constructors.
As an example, maybe refactor something like this:
Change your base Deck to just take the set of cards:
public abstract class Deck
{
public List<Card> Cards;
protected Deck(IEnumerable<Card> cards)
{
this.Cards = new List<Card>(cards);
}
}
Then have the factory setup like this:
public class EuchreDeck : Deck
{
private EuchreDeck(IEnumerable<Card> cards) : base(cards)
{
}
public class Factory : DeckFactory
{
private static readonly string[] Values = new string[] { "9", "10", "J", "Q", "K", "A" };
private static readonly string[] Suits = new string[] { "clubs", "spades", "hearts", "diamonds" };
public static EuchreDeck Create()
{
var cards = CreateCards(Values, Suits);
return new EuchreDeck(cards);
}
}
}
Instantiation/usage as:
EuchreDeck.Factory.Create();
You could play around with the factory usage. I just nested it in the class so you couldn't create a EuchreDeck with an invalid set of cards. Your DeckFactory base would have your conversion method (which looks like you currently have in your Deck constructor)
Beyond that, I'm not sure if you have a specific need for a EuchreDeck; I'm assuming you have other methods associated with it? If not, you could probably ditch the class altogether and just let the factory create a Deck with the needed cards.
I think the problem is that the values and suits should be declared static.
static string[] values = new string[] { "9", "10", "J", "Q", "K", "A" };
static string[] suits = new string[] { "clubs", "spades", "hearts", "diamonds" };
That way, they will be available at when instantiating the new class.
You have to have somewhere that variable decalred :
public EuchreDeck() : base(values, suits) // Error.
{}
where are values and suits declared?
You have to define them static, like:
static readonly string[] values = new string[] { "9", "10", "J", "Q", "K", "A" };
static readonly string[] suits = new string[]
{ "clubs", "spades", "hearts", "diamonds" };
The thing is that base(..) can only access values visible in ctor scope. So you can not pass instance variables, as instance is not accessible on that level, but you can pass ctor parameters and static members of calling class.

Return a constant string array

I created a string[] getter to get some information on a class. I want it to always return the same value, and not create a new object on each call.
I have it implemented now like this:
string[] _someStrings = { "foo", "bar" };
protected string[] someStrings {
get {
return _someStrings;
}
}
which seem to be OK. However, my first inkling was to write it like this:
protected string[] someStrings {
get {
return { "foo", "bar" };
}
}
but that doesn't work (I get the error ; expected).
Why?
(this is mainly a "getting-to-understand-C# question).
update I made a typo. I do not want to create a new object on each call.
The correct syntax would be this:
return new [] { "foo", "bar" };
The reason is that the short syntax without new [] is only valid for an assignment.
As you correctly note in a comment, this will create a new object on every call. The only way to avoid this is with a field that stores the created instance and return that field. This is exactly the solution you already have.
Please note however, that this allows consumers to change the contents of the array and affect other consumers:
var a1 = foo.SomeStrings;
var a2 = foo.SomeStrings;
a1[0] = "Some other value";
Assert.Equal("Some other value", a2[0]); // will pass
As an alternate approach, may I suggest, if the contents are supposed to be constant, using a read-only collection instead, such that:
private readonly ReadOnlyCollection<string> UnderlyingReadOnlyStrings;
// populate the read-only collection, then...
public ReadOnlyCollection<string> ReadOnlyStrings {
get { return UnderlyingReadOnlyStrings; }
}
The benefit here is that your collection truly is read-only. And practically constant. It can't be re-assigned to, and the contents cannot be altered. You could even declare the underlying collection as static and populate in a static constructor.
Your second example doesn't work, as previously explained, because you're trying to return an "inline array", so to speak, and the syntax is not correct, and if it were, you would be newing the array each time - which goes against your requirements.
Your syntax is incorrect. Try this:
protected string[] someStrings
{
get
{
return new string[] { "foo", "bar" };
}
}
You can't have const array, but you can have a readonly one that will work as you expect (can also be static, obviously):
public readonly string[] someStrings = { "foo", "bar" };

IDictionary assignment shortcuts compiler feature or language feature?

Through some random object creation today I came across this neat little shortcut for a Dictionary<K, V>. Is the following assignment a compiler shortcut or is it a feature of Dictionary<string, string>.
IDictionary<string, string> items = { { "item1key", "item1value" } };
Looking at the source for Dictionary<K, V> I don't see anything offhand for how this works. Implementing all the interfaces for this class dot not allow me to perform a similar operation. Why is it that we can do it for a dictionary but not another type. For example, how does the compiler or language feature know that the first item is a key and the second item is the value. Or even more specific this same syntax can't be used for a List<string>
List<string> items = { "item1" };
So the first is valid, why?
I'm not necessarily trying to duplicate this but rather curious as to why it is the way it is. What makes a dictionary special in this case?
Example that works
public class Button
{
public string Title { get; set; }
public ButtonType Type { get; set; }
public IDictionary<string, string> Items { get; set; }
public bool RequiresSelected { get; set; }
}
var buttons = new List<Button>
{
new Button {
Items = {
{"button1", "Button 1"},
{"button2", "Button 2"},
{"button3", "Button 3"},
},
Title = "3 Buttons",
Type = ButtonType.DropDown
}
};
The syntax you've shown isn't valid in C#. You'd need:
IDictionary<string, string> items = new Dictionary<string, string>
{ { "item1key", "item1value" } };
At that point it's just a normal collection initializer, so the list equivalent would be:
List<string> items = new List<string> { "item1" };
EDIT: Let's see if my edit can beat yours. My guess is that you've seen something like:
var foo = new Foo {
SomeDictionaryProperty = {
{ "item1key", "item1value" }
}
};
That's an embedded collection initializer, and can be used for lists too. It's not creating a new dictionary, it's adding to an existing one. The code above is equivalent to:
var tmp = new Foo();
tmp.SomeDictionaryProperty.Add("item1key", "item1value");
var foo = tmp;
Another example of it working:
var form = new Form {
Controls = { new Label { Text = "Foo"}, new Label { Text = "Bar" } }
};
See section 7.6.10.2 of the C# 4 specification (Object Initializers) for more information. The important bit is this:
member-initializer:
identifier = initializer-value
initializer-value:
expression
object-or-collection-initializer
So you can initialize a property to either be a specific value (in which case the setter will be used) or via an object/collection initializer, in which case the getter for the property will be used, and then setters or the Add method will be used for the body of the object/collection initializer.
This is a feature of C# compiler, and the dictionary is not special: any collection that supports Add can be initialized in this way. The details are in the section 7.6.10.3 of the C# Language Specification 4.0.
The collection object to which a collection initializer is applied must be of a type that implements System.Collections.IEnumerable or a compile-time error occurs. For each specified element in order, the collection initializer invokes an Add method on the target object with the expression list of the element initializer as argument list, applying normal overload resolution for each invocation. Thus, the collection object must contain an applicable Add method for each element initializer.

C# object initialization of read only collection properties

For the life of me, I cannot figure out what is going on in the example piece of C# code below. The collection (List) property of the test class is set as read only, but yet I can seemingly assign to it in the object initializer.
** EDIT: Fixed the problem with the List 'getter'
using System;
using System.Collections.Generic;
using NUnit.Framework;
namespace WF4.UnitTest
{
public class MyClass
{
private List<string> _strCol = new List<string> {"test1"};
public List<string> StringCollection
{
get
{
return _strCol;
}
}
}
[TestFixture]
public class UnitTests
{
[Test]
public void MyTest()
{
MyClass c = new MyClass
{
// huh? this property is read only!
StringCollection = { "test2", "test3" }
};
// none of these things compile (as I wouldn't expect them to)
//c.StringCollection = { "test1", "test2" };
//c.StringCollection = new Collection<string>();
// 'test1', 'test2', 'test3' is output
foreach (string s in c.StringCollection) Console.WriteLine(s);
}
}
}
This:
MyClass c = new MyClass
{
StringCollection = { "test2", "test3" }
};
is translated into this:
MyClass tmp = new MyClass();
tmp.StringCollection.Add("test2");
tmp.StringCollection.Add("test3");
MyClass c = tmp;
It's never trying to call a setter - it's just calling Add on the results of calling the getter. Note that it's also not clearing the original collection either.
This is described in more detail in section 7.6.10.3 of the C# 4 spec.
EDIT: Just as a point of interest, I was slightly surprised that it calls the getter twice. I expected it to call the getter once, and then call Add twice... the spec includes an example which demonstrates that.
You aren't calling the setter; you are essentially calling c.StringCollection.Add(...) each time (for "test2" and "test3") - it is a collection initializer. For it to be the property assignment, it would be:
// this WON'T work, as we can't assign to the property (no setter)
MyClass c = new MyClass
{
StringCollection = new StringCollection { "test2", "test3" }
};
I think that, beeing read only, you can't do
c.StringCollection = new List<string>();
But you can assign items to list...
Am I wrong?
The StringCollection property doesn't have a setter so unless you add one you cannot modify its value.

Categories

Resources