I'm currently studying indexers chapter, but I'm unable to understand "this[int pos]" and "this[string data]" of the following code. Could anyone help me in this?
class OvrIndexer
{
private string[] myData;
private int arrSize;
public OvrIndexer(int size)
{
arrSize = size;
myData = new string[size];
for (int i=0; i < size; i++)
{
myData[i] = "empty";
}
}
public string this[int pos]
{
get
{
return myData[pos];
}
set
{
myData[pos] = value;
}
}
public string this[string data]
{
get
{
int count = 0;
for (int i=0; i < arrSize; i++)
{
if (myData[i] == data)
{
count++;
}
}
return count.ToString();
}
set
{
for (int i=0; i < arrSize; i++)
{
if (myData[i] == data)
{
myData[i] = value;
}
}
}
}
One accesses the index by integer, and the other by string:
var indexer = new OvrIndexer();
// Sets the first item of the indexer to "Value1"
indexer[0] = "Value1";
// Sets the item identified by key "Key2" to value "Value2"
indexer["Key2"] = "Value2";
This is just the defining of indexers for your class, so you can use syntax like this to get the contents of the class internal array.
myOvrIndexer[3]
will return the 4th element of the myData array.
myOvrIndexer["Test"]
will return the first element with the content Test.
Note that this class is mostly useless as it just wraps a array and do not add any useful functionality except the indexer receiving a string instead of index, but from a learning perspective this class should do a good job explaining whats going on.
The main purpose of the indexers is to avoid having to create methods, and thereby having to write syntax like this:
myOvrIndexer.GetElement(3);
myOvrIndexer.SetElement(3, myValue);
I think we both agree that this syntax looks better:
myOvrIndexer[3];
myOvrIndexer[3] = myValue;
this[int pos]
The getter will return the value at index specified.
The setter will set the value at the index specified.
whereas
this[string data]
The getter will return the count of occurances of the value you are looking up.
The setter will replace all value matches found with the new value.
Related
I know, the title is bad, but I couldn't think if a better one. The question is very specific.
Ok, so I'm using a class in my game identical to Odin Inspector's example RPG Skills classes. But it's set up in a way I don't quite understand and I can't work out how to set the value (I can get it, and there is a setter, so it's possible to set too). Also, all the skill classes/structs/etc are in the same .cs file.
The SkillList function I use to get the Value:
(I get it with skills[Strength].Value; in other classes)
public int this[SkillType type]
{
get
{
for (int i = 0; i < this.skills.Count; i++)
{
if (this.skills[i].Type == type)
return this.skills[i].Value;
}
return 0;
}
set
{
for (int i = 0; i < this.skills.Count; i++)
{
if (this.skills[i].Type == type)
{
var val = this.skills[i];
val.Value = value;
this.skills[i] = val;
return;
}
}
this.skills.Add(new SkillValue(type, value));
}
}
SkillValue struct:
[Serializable]
public struct SkillValue : IEquatable<SkillValue>
{
public SkillType Type;
public int Value;
public SkillValue(SkillType type, int value)
{
this.Type = type;
this.Value = value;
}
public SkillValue(SkillType type)
{
this.Type = type;
this.Value = 0;
}
public bool Equals(SkillValue other)
{ return this.Type == other.Type && this.Value == other.Value; }
}
SkillType enum:
public enum SkillType
{
Science,
Technology,
Arts,
Language,
}
I've tried:
skills[Science].Value = 10;
skills[Science] = new SkillValue(Science, 10);
skills[Science, 10]; (using a new function made by me)
skills[Science](10);
skills[Science].Value(10);
skills[Science] = 10;
But none work, and I'm just guessing randomly now.
How can I set the value?
Thanks
The solution:
character.skills[Rorschach.Character.Skills.SkillType.Science] = value;
Your property is of type int and expects a key of type SkillType so it should probably be
SkillList skills;
skills[SkillType.Science] = 10;
actually also
I get it with skills[Strength].Value;
seems odd with the code you provided. As said the property returns an int which has no property Value so it should actually be
int x = skills[SkillType.Strength];
Now knowing the full implementation code and your actual usage:
public SkillList skills;
...
public int Science
{
get { return this.Character.skills[Science].Value; }
set { this.Character.skills[Science].Value(10); }
}
ATTENTION!
What you did here by accident is using the other property
public SkillValue this[int index]
{
get { return this.skills[index]; }
set { this.skills[index] = value; }
}
which takes an int index and returns a SkillValue.
BUT you are causing a runtime StackOverlowExeption due to a recursive call of Science.
You can't use Science inside of the getter or setter of equally called property!
Imagine using the getter as example:
You would call
var test = Science;
so it executes the getter
return Character.skills[Science].Value;
but well ... in order to know the value of Science in order to use it here as the index it would again have to execute the getter so again
return Character.skills[Science].Value;
and by now you hopefully get what I mean.
Solution
You property should actually as guessed before rather look like
public int Science
{
get { return Character.skills[SkillType.Science]; }
set { Character.skills[SkillType.Science] = value; }
}
I am declaring two private arrays in indexes and displaying the data in main. However, it is not displaying any one tell me how to display the data in the two private arrays in indexers?
class Program
{
static void Main(string[] args)
{
student sc = new student();
for (int i = 0; i < sc.mlength; i++)
{
Console.WriteLine(sc[i]);
}
Console.ReadLine();
//i am declaring two private arrays in indexes and displaying the data in main is not displaying any one tell me how to display the data in the two private arrays in indexers?
}
}
public class student
{
private int[] _marks = new int[] { 60, 68, 70 };
private string[] _names = new string[] { "suri", "kumar", "suresh" };
public int this[int i]
{
get
{
return _marks[i];
}
set
{
_marks[i] = value;
}
}
public string this[int i]
{
get
{
return _names[Convert.ToInt32(i)];
}
set
{
_names[Convert.ToInt32(i)] = value;
}
}
public int mlength
{
get
{
return _marks.Length;
}
}
public int nlenght
{
get
{
return _names.Length;
}
}
}
}
Indexers allow your class to be used just like an array. On the inside of a class, you manage a collection of values any way you want. These objects could be a finite set of class members, another array, or some complex data structure. Regardless of the internal implementation of the class, its data can be obtained consistently through the use of indexers. Here’s an example.
Example:
using System;
class IntIndexer
{
private string[] myData;
public IntIndexer(int size)
{
myData = new string[size];
for (int i=0; i < size; i++)
{
myData[i] = "empty";
}
}
public string this[int pos]
{
get
{
return myData[pos];
}
set
{
myData[pos] = value;
}
}
static void Main(string[] args)
{
int size = 10;
IntIndexer myInd = new IntIndexer(size);
myInd[9] = "Some Value";
myInd[3] = "Another Value";
myInd[5] = "Any Value";
Console.WriteLine("\nIndexer Output\n");
for (int i=0; i < size; i++)
{
Console.WriteLine("myInd[{0}]: {1}", i, myInd[i]);
}
}
}
The IntIndexer class has a string array named myData. This is a private array that external users can’t see. This array is initialized in the constructor, which accepts an int size parameter, instantiates the myData array, and then fills each element with the word “empty”.
The IntIndexer class has a string array named myData. This is a private array that external users can’t see. This array is initialized in the constructor, which accepts an int size parameter, instantiates the myData array, and then fills each element with the word “empty”.
The next class member is the Indexer, which is identified by the this keyword and square brackets, this[int pos]. It accepts a single position parameter, pos. As you may have already guessed, the implementation of an Indexer is the same as a Property. It has get and setaccessors that are used exactly like those in a Property. This indexer returns a string, as indicated by the string return value in the Indexer declaration.
The Main() method simply instantiates a new IntIndexer object, adds some values, and prints the results. Here’s the output:
Indexer Output
myInd[0]: empty
myInd[1]: empty
myInd[2]: empty
myInd[3]: Another Value
myInd[4]: empty
myInd[5]: Any Value
myInd[6]: empty
myInd[7]: empty
myInd[8]: empty
myInd[9]: Some Value
So I have a CreditCard class that has some properties, one of which is for the card number as a String (public string Number { get; set; }). I'm storing the CreditCard objects in a CreditCardList class which has the variable List (private List<CreditCard> cclist = new List<CreditCard>();). I want to be able to retrieve a CreditCard by its card number by sorting the List first, then using the BinarySearch method on the List. I also want to do this by passing a String indexer of the number to search for into the BinarySearch method, along with a comparer if I need one.
This is what I have so far for the method to get the CreditCard matching the number, but Visual Studio 2013 gives me an error on the line: int index = cclist.BinarySearch(cclist[input], new CreditCardComparer()); "the best overloaded method match for 'System.Collections.Generic.List.this[int]' has some invalid arguments." I assume it's because I'm using the String indexer wrong or something.
public List<CreditCard> GetCardByNumber (string input)
{
List<CreditCard> tempList = new List<CreditCard>();
// save the current unsorted list to a temporary list to revert back to after sorting
List<CreditCard> originalList = new List<CreditCard>(cclist.Capacity);
for (int i = 0; i < cclist.Capacity; i++)
{
originalList[i] = cclist[i];
}
// begin sorting for binary search of card number
cclist.Sort();
int index = cclist.BinarySearch(cclist[input], new CreditCardComparer());
if (index < 0)
{
tempList.Add(cclist[input]);
}
// revert back to the original unsorted list
for (int i = 0; i < originalList.Capacity; i++)
{
cclist[i] = originalList[i];
}
// return the found credit card matching the specified number
return tempList;
}// end GetCardByNumber (string input)
Here are my int and string indexers:
public CreditCard this[int i]
{
get
{
if (i < 0 || i >= cclist.Count)
{
throw new ArgumentOutOfRangeException("index " + i + " does not exist");
}
return cclist[i];
}
set
{
if (i < 0 || i >= cclist.Count)
{
throw new ArgumentOutOfRangeException("index " + i + " does not exist");
}
cclist[i] = value;
saveNeeded = true;
}
}// end CreditCard this[int i]
public CreditCard this[string input]
{
get
{
foreach (CreditCard cc in cclist)
{
if (cc.Number == input)
{
return cc;
}
}
return null;
}
}// end CreditCard this[string number]
And here is my comparer class:
public class CreditCardComparer : IComparer<CreditCard>
{
public override int Compare(CreditCard x, CreditCard y)
{
return x.Number.CompareTo(y.Number);
}
}// end CreditCardComparer : IComparer<CreditCard>
And lastly, here are the necessities for my list sorting and what not...
class CreditCard : IEquatable<CreditCard>, IComparable<CreditCard>
{
public bool Equals (CreditCard other)
{
if (this.Number == other.Number)
{
return true;
}
else
{
return false;
}
}// end Equals(CreditCard other)
public int CompareTo(CreditCard other)
{
return Number.CompareTo(other.Number);
}// end CompareTo(CreditCard other)
}
Is it truly possible to do what I'm attempting, i.e. sending a string indexer that returns a CreditCard object based on a string into a BinarySearch method of List?
Also, I can provide any more code if necessary, but I felt like this was a little much to begin with.
A System.Collections.Generic.List uses an int as the indexer property, it does not let you use a string.
If you want to use a string as your indexer (primary key), you should probably use a Dictionary<string,CreditCard> instead.
There are a couple things amiss in your GetCardByNumber method. First is the method returns an entire list instead of a single CreditCard, which goes against the method name. Second, the binary search is not even needed since you do the searching in the string indexer first:
public CreditCard this[string input]
{
get
{
foreach (CreditCard cc in cclist)
{
if (cc.Number == input)
{
return cc;
}
}
return null;
}
}
By this point, you've already found the CreditCard with the information you need, so why search for it again in a BinarySearch? Thirdly, as was covered in landoncz's answer, you can't use a string as an index for a List<T>. What you probably intended to use was the CreditCardList instead of the List<CreditCard>
CreditCardList creditCardList = new CreditCardList();
creditCardList["1234"]; //correct
List<CreditCard> cclist = new List<CreditCard>();
cclist["1234"]; //incorrect. This is where your error is coming from.
If you're trying to access the indexer property inside of the class that implements it (which I'm assuming you are trying to do in your GetCardByNumber method), just use this[index]:
public class CreditCardList
{
public CreditCard this[string s] { /*Implementation*/ }
public CreditCard GetCard(string s)
{
return this[s]; // right here!
}
}
Now... according to your comment, "Retrieve the CreditCard with a specified number if it exists using the BinarySearch method in List<T> in the implementation of a String indexer.", it seems to me that the assignment wants you doing something along these lines. (a thing to note is that I'm not sure of your entire implementation of the CreditCard class, so please excuse the naive instantiation in the following code)
public class CreditCardList
{
private List<CreditCard> cclist = new List<CreditCard>();
public CreditCardList()
{
//For the sake of an example, let's magically populate the list.
MagicallyPopulateAList(cclist);
}
public CreditCard this[string s] /* In the implementation of a String indexer... */
{
get
{
CreditCard ccToSearchFor = new CreditCard() { Number = value };
cclist.Sort();
/* ...use the BinarySearch method... */
int index = cclist.BinarySearch(ccToSearchFor);
if (index >= 0)
return cclist[index]; /* ...to retrieve a CreditCard. */
else
throw new ArgumentException("Credit Card Number not found.");
}
}
}
I have a situation where I need to do many gets/sets, after perfomance analysis this is one of the more expensive parts of my application. Origionally I was using a Dictionary but switched to a jagged array internally which gave it a signficant perfomance boost, still I'd like to see if theres a way to improve the perfomance of this code without ditching the nice and useable syntax.
Note calling Convert.ToInt32 is signficantly more expensive than calling a cast, and since the generic constraint TStatus : int doesn't work for an enum I had to implement this as a abstract class, it would be nice if this collection would work with any enum out of the box.
Also I tried implementing yield for the IEnumerable, however that was actually slower than just populating a list.
public abstract class LoanStatusVectorOverTime<TStatus> : ILoanStatusVectorOverTime<TStatus>
where TStatus: struct
{
protected static readonly TStatus[] LoanStatusTypes = (TStatus[])Enum.GetValues(typeof(TStatus));
protected static readonly int LoanStatusCount = Enum.GetValues(typeof(TStatus)).Length;
protected const int MonthsSinceEventCount = 25;
private readonly object SYNC = new object();
protected double[,] VectorDictionary { get; set; }
public LoanStatusVectorOverTime()
{
this.VectorDictionary = new double[LoanStatusCount, MonthsSinceEventCount];
}
public double this[TStatus status, int monthsSince]
{
get
{
if (monthsSince >= MonthsSinceEventCount)
return 0;
return VectorDictionary[GetKeyValue(status), monthsSince];
}
set
{
if (monthsSince >= MonthsSinceEventCount)
return;
VectorDictionary[GetKeyValue(status), monthsSince] = value;
}
}
public double SumOverStatus(TStatus status)
{
double sum = 0;
foreach (var fromStatus in LoanStatusTypes)
{
int i = 0;
while (i < MonthsSinceEventCount)
{
sum += VectorDictionary[GetKeyValue(fromStatus), i];
i++;
}
}
return sum;
}
public IEnumerator<KeyValuePair<Tuple<TStatus, int>, double>> GetEnumerator()
{
List<KeyValuePair<Tuple<TStatus, int>, double>> data = new List<KeyValuePair<Tuple<TStatus, int>, double>>();
foreach (var fromStatus in LoanStatusTypes)
{
int i = 0;
while (i < MonthsSinceEventCount)
{
var val = VectorDictionary[GetKeyValue(fromStatus), i];
if (val != default(double))
data.Add(new KeyValuePair<Tuple<TStatus, int>, double>(new Tuple<TStatus, int>(fromStatus, i), val));
i++;
}
}
return data.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
protected abstract int GetKeyValue(TStatus status);
protected abstract ILoanStatusVectorOverTime<TStatus> Initalize();
public ILoanStatusVectorOverTime<TStatus> Copy()
{
var vect = Initalize();
foreach (var fromStatus in LoanStatusTypes)
{
int i = 0;
while (i < MonthsSinceEventCount)
{
vect[fromStatus, i] = VectorDictionary[GetKeyValue(fromStatus), i];
i++;
}
}
return vect;
}
public double SumOverAll(int monthsSince = 1)
{
double sum = 0;
foreach (var status in LoanStatusTypes)
{
sum += this[status, monthsSince];
}
return sum;
}
}
public class AssetResolutionVector : LoanStatusVectorOverTime<AssetResolutionStatus>
{
protected override int GetKeyValue(AssetResolutionStatus status)
{
return (int)status;
}
protected override ILoanStatusVectorOverTime<AssetResolutionStatus> Initalize()
{
return new AssetResolutionVector();
}
}
var arvector = new AssetResolutionVector();
arvector[AssetResolutionStatus.ShortSale, 1] = 10;
If the enum to int conversion is taking up a lot of your time, make sure that don't do the conversion during every iteration of your inner loop. Here's an example of the conversion getting cached for your SumOverStatus method:
public double SumOverStatus(TStatus status)
{
double sum = 0;
foreach (var fromStatus in LoanStatusTypes)
{
int statusKey = GetKeyValue(fromStatus);
int i = 0;
while (i < MonthsSinceEventCount)
{
sum += VectorDictionary[statusKey, i];
i++;
}
}
return sum;
}
Extra tip: although it may not have give a performance boost, you can avoid making your class abstract by using a Func<TStatus, int> converter. Here's how the converter could be exposed as a property (a constructor argument would work fine too):
public class LoanStatusVectorOverTime<TStatus>
{
public Func<TStatus, int> GetKeyValue { get; set; }
}
// When the object gets instantiated
loanStatusVectorOverTime.GetKeyValue = status => (int)status;
It sounds like you have two separate problems here:
Converting from an enum to an integer has an overhead when the conversion is being done using an abstract method or a delegate. You have two options here:
A. Take out the generic parameter from your class and hardcode the enum type (making multiple copies of the class if necessary).
B. Have your accessors take two integers instead of an enum and an integer (letting the client do a cheap cast from enum to integer).
A lot of time is being used during get/set. This may not be because get/set are inefficient, but because get/set are being called too many times. Suggestions:
A. Group your operations by month or by status, restructure your data structures (maybe using nested arrays), and write efficient loops.
B. Reduce the computational complexity of the code that is doing all the getting and setting. By stepping back and mapping out your program, you may find more efficient algorithms.
in the following indexer code block, why do we need:
public string this[int pos]
{
get
{
return myData[pos];
}
set
{
myData[pos] = value;
}
}
what exactly does "this" in this[int pos] do? Thanks
/// Indexer Code Block starts here
using System;
/// <summary>
/// A simple indexer example.
/// </summary>
class IntIndexer
{
private string[] myData;
public IntIndexer(int size)
{
myData = new string[size];
for (int i = 0; i < size; i++)
{
myData[i] = "empty";
}
}
public string this[int pos]
{
get
{
return myData[pos];
}
set
{
myData[pos] = value;
}
}
static void Main(string[] args)
{
int size = 10;
IntIndexer myInd = new IntIndexer(size);
myInd[9] = "Some Value";
myInd[3] = "Another Value";
myInd[5] = "Any Value";
Console.WriteLine("\nIndexer Output\n");
for (int i = 0; i < size; i++)
{
Console.WriteLine("myInd[{0}]: {1}", i, myInd[i]);
}
}
}
It means that you can use the indexer on the object itself (like an array).
class Foo
{
public string this[int i]
{
get { return someData[i]; }
set { someData i = value; }
}
}
// ... later in code
Foo f = new Foo( );
string s = f[0];
From a c# syntax perspective:
You need this because - how else would you declare it? Functionality on a class must have a name, or address, by which to reference it.
A method signature is:
[modifiers] [type] [name] (parameters)
public string GetString (Type myparam);
A property signature is:
[modifiers] [type] [name]
public string MyString
A field signature is:
[modifiers] [type] [name]
public string MyString
Since an indexer has no name, it would not make much sense to write:
public string [int pos]
So we use this to denote it's "name".
This is just marker for compiler to know that that property has indexer syntax.
In this case it enables myInd to use "array syntax" (e.g. myInd[9]).
The 'this' keyword indicates that you are defining behavior that will be invoked when your class is accessed as if it was an array. Since your defining behavior for the class instance, use of the 'this' keyword in that context makes sense. You don't call myInd.indexer[], you call myInd[].
It allows your class to behave in a similar manner to an array. In this case, your indexer is allowing you to reference myData transparently from outside the IntIndexer class.
If you didn't have the indexer declared, the following code would fail:
myInd[1] = "Something";
The "this" in your case specifies that this property is the indexer for this class. This is the syntax in C# to define an indexer on a class, so you can use it like:
myInd[9] = ...