I'm working on a C# object copy constructor, part of which involves copying the contents of a KeyedCollection into a new KeyedCollection. This is what I have implemented currently:
class MyKeyedCollection : KeyedCollection<uint, DataObject>
{
protected override uint GetKeyForItem( DataObject do )
{
return do.Key;
}
}
class MyObject
{
private MyKeyedCollection kc;
// Copy constructor
public MyObject( MyObject that )
{
this.kc = new MyKeyedCollection();
foreach ( DataObject do in that.kc )
{
this.kc.Add( do );
}
}
}
This does the correct thing -- the collection is copied as expected. The problem is that it's also a bit slow. I'm guessing that the problem is that each .Add(do) requires a uniqueness check on the existing data, even though I know it's coming from a source that guarantees uniqueness.
How can I make this copy constructor as fast as possible?
Ok, how about a solution with a little unsafe code? Just for fun?
WARNINGS! This is coded for windows OS and 32 bit, but there is no reason this technique can't be modified to work for 64 bit or other OS's. Finally, I tested this on 3.5 framework. I think it will work on 2.0 and 3.0 but I didn't test. If Redmond changes the number, type, or order of instance variables between the revisions or patches, then this won't work.
But this is fast!!!
This hacks into the KeyedCollection, its underlying List<> and Dictionary<> and copies all the internal data and properties. Its a hack because to do this you have to access private internal variables. I basicly made some structures for KeyedCollection, List, and Dictionary that are those classes' private variables in the right order. I simply point an these structures to where the classes are and voila...you can mess with the private variables!! I used the RedGate reflector to see what all the code was doing so I could figure out what to copy. Then its just a matter of copying some value types and using Array.Copy in a couple places.
The result is CopyKeyedCollection<,>, CopyDict<> and CopyList<>. You get a function that can quick copy a Dictionary<> and one that can quick copy a List<> for free!
One thing I noticed when working this all out was that KeyedCollection contains a list and a dictionary all pointing to the same data! I thought this was wasteful at first, but commentors pointed out KeyedCollection is expressly for the case where you need an ordered list and a dictionary at the same time.
Anyway, i'm an assembly/c programmer who was forced to use vb for awhile, so I am not afraid of doing hacks like this. I'm new to C#, so tell me if I have violated any rules or if you think this is cool.
By the way, I researched the garbage collection, and this should work just fine with the GC. I think it would be prudent if I added a little code to fix some memory for for the ms we spend copying. You guys tell me. I'll add some comments if anyone requests em.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Collections.ObjectModel;
using System.Reflection;
namespace CopyCollection {
class CFoo {
public int Key;
public string Name;
}
class MyKeyedCollection : KeyedCollection<int, CFoo> {
public MyKeyedCollection() : base(null, 10) { }
protected override int GetKeyForItem(CFoo foo) {
return foo.Key;
}
}
class MyObject {
public MyKeyedCollection kc;
// Copy constructor
public MyObject(MyObject that) {
this.kc = new MyKeyedCollection();
if (that != null) {
CollectionTools.CopyKeyedCollection<int, CFoo>(that.kc, this.kc);
}
}
}
class Program {
static void Main(string[] args) {
MyObject mobj1 = new MyObject(null);
for (int i = 0; i < 7; ++i)
mobj1.kc.Add(new CFoo() { Key = i, Name = i.ToString() });
// Copy mobj1
MyObject mobj2 = new MyObject(mobj1);
// add a bunch more items to mobj2
for (int i = 8; i < 712324; ++i)
mobj2.kc.Add(new CFoo() { Key = i, Name = i.ToString() });
// copy mobj2
MyObject mobj3 = new MyObject(mobj2);
// put a breakpoint after here, and look at mobj's and see that it worked!
// you can delete stuff out of mobj1 or mobj2 and see the items still in mobj3,
}
}
public static class CollectionTools {
public unsafe static KeyedCollection<TKey, TValue> CopyKeyedCollection<TKey, TValue>(
KeyedCollection<TKey, TValue> src,
KeyedCollection<TKey, TValue> dst) {
object osrc = src;
// pointer to a structure that is a template for the instance variables
// of KeyedCollection<TKey, TValue>
TKeyedCollection* psrc = (TKeyedCollection*)(*((int*)&psrc + 1));
object odst = dst;
TKeyedCollection* pdst = (TKeyedCollection*)(*((int*)&pdst + 1));
object srcObj = null;
object dstObj = null;
int* i = (int*)&i; // helps me find the stack
i[2] = (int)psrc->_01_items;
dstObj = CopyList<TValue>(srcObj as List<TValue>);
pdst->_01_items = (uint)i[1];
// there is no dictionary if the # items < threshold
if (psrc->_04_dict != 0) {
i[2] = (int)psrc->_04_dict;
dstObj = CopyDict<TKey, TValue>(srcObj as Dictionary<TKey, TValue>);
pdst->_04_dict = (uint)i[1];
}
pdst->_03_comparer = psrc->_03_comparer;
pdst->_05_keyCount = psrc->_05_keyCount;
pdst->_06_threshold = psrc->_06_threshold;
return dst;
}
public unsafe static List<TValue> CopyList<TValue>(
List<TValue> src) {
object osrc = src;
// pointer to a structure that is a template for
// the instance variables of List<>
TList* psrc = (TList*)(*((int*)&psrc + 1));
object srcArray = null;
object dstArray = null;
int* i = (int*)&i; // helps me find things on stack
i[2] = (int)psrc->_01_items;
int capacity = (srcArray as Array).Length;
List<TValue> dst = new List<TValue>(capacity);
TList* pdst = (TList*)(*((int*)&pdst + 1));
i[1] = (int)pdst->_01_items;
Array.Copy(srcArray as Array, dstArray as Array, capacity);
pdst->_03_size = psrc->_03_size;
return dst;
}
public unsafe static Dictionary<TKey, TValue> CopyDict<TKey, TValue>(
Dictionary<TKey, TValue> src) {
object osrc = src;
// pointer to a structure that is a template for the instance
// variables of Dictionary<TKey, TValue>
TDictionary* psrc = (TDictionary*)(*((int*)&psrc + 1));
object srcArray = null;
object dstArray = null;
int* i = (int*)&i; // helps me find the stack
i[2] = (int)psrc->_01_buckets;
int capacity = (srcArray as Array).Length;
Dictionary<TKey, TValue> dst = new Dictionary<TKey, TValue>(capacity);
TDictionary* pdst = (TDictionary*)(*((int*)&pdst + 1));
i[1] = (int)pdst->_01_buckets;
Array.Copy(srcArray as Array, dstArray as Array, capacity);
i[2] = (int)psrc->_02_entries;
i[1] = (int)pdst->_02_entries;
Array.Copy(srcArray as Array, dstArray as Array, capacity);
pdst->_03_comparer = psrc->_03_comparer;
pdst->_04_m_siInfo = psrc->_04_m_siInfo;
pdst->_08_count = psrc->_08_count;
pdst->_10_freeList = psrc->_10_freeList;
pdst->_11_freeCount = psrc->_11_freeCount;
return dst;
}
// these are the structs that map to the private variables in the classes
// i use uint for classes, since they are just pointers
// statics and constants are not in the instance data.
// I used the memory dump of visual studio to get these mapped right.
// everything with a * I copy. I Used RedGate reflector to look through all
// the code to decide what needed to be copied.
struct TKeyedCollection {
public uint _00_MethodInfo; // pointer to cool type info
// Collection
public uint _01_items; // * IList<T>
public uint _02_syncRoot; // object
// KeyedCollection
public uint _03_comparer; // IEqualityComparer<TKey>
public uint _04_dict; // * Dictionary<TKey, TItem>
public int _05_keyCount; // *
public int _06_threshold; // *
// const int defaultThreshold = 0;
}
struct TList {
public uint _00_MethodInfo; //
public uint _01_items; // * T[]
public uint _02_syncRoot; // object
public int _03_size; // *
public int _04_version; //
}
struct TDictionary {
// Fields
public uint _00_MethodInfo; //
public uint _01_buckets; // * int[]
public uint _02_entries; // * Entry<TKey, TValue>[]
public uint _03_comparer; // IEqualityComparer<TKey>
public uint _04_m_siInfo; // SerializationInfo
public uint _05__syncRoot; // object
public uint _06_keys; // KeyCollection<TKey, TValue>
public uint _07_values; // ValueCollection<TKey, TValue>
public int _08_count; // *
public int _09_version;
public int _10_freeList; // *
public int _11_freeCount; // *
}
}
}
I just ran a test adding 10,000,000 items and to various collections, and the KeyedCollection took about 7x as long as a list, but only about 50% longer than a Dictionary object. Considering that the KeyedCollection is a combination of these two, the performance of Add is perfectly reasonable, and the duplicate-key check it runs is clearly not taking that much time. You might want to run a similar test on your KeyedCollection, and if it's going significantly slower, you can start looking elsewhere (check your MyObject.Key getter to make sure you're not getting overhead from that).
Old Response
Have you tried:
this.kc = that.kc.MemberwiseClone() as MyKeyedCollection;
More info on MemberwiseClone here.
You could try to serialize the object and then deserialize it into a new object - I cannot tell if this will gain any performance but it might.
If you are doing this a lot it suggests you should instead be using immutable collections.
These are structures which you do not modify directly but instead 'modifications' return a new object which may use the old objects state but reflect the changes you made.
Various immutable dictionaries/sets/tree based maps are available for .Net (many in f# however as it is more amenable to this style of development)
Eric Lippert has some excellent articles on this and the AVL Tree should be close to exactly what you want.
///
/// Clones Any Object.
/// </summary>
/// <param name="objectToClone">The object to clone.</param>
/// <return>The Clone</returns>
public static T Clone<T>(T objectToClone)
{
T cloned_obj = default(T);
if ((!Object.ReferenceEquals(objectToClone, null)) && (typeof(T).IsSerializable))
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bin_formatter = null;
Byte[] obj_bytes = null;
using (MemoryStream memory_stream = new MemoryStream(1000))
{
bin_formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
try
{
bin_formatter.Serialize(memory_stream, objectToClone);
}
catch (SerializationException) { }
obj_bytes = memory_stream.ToArray();
}
using (MemoryStream memory_stream = new MemoryStream(obj_bytes))
{
try
{
cloned_obj = (T)bin_formatter.Deserialize(memory_stream);
}
catch (SerializationException) { }
}
}
return cloned_obj;
}
Note: The objectToClone must be Serializable, otherwise you will hit the exceptions and null will be returned.
You will also need to create your own IDataObject because DataObject is not Serializable:
[Serializable]
public class MyDataObject : IDataObject
{
public int mData;
public MyDataObject(int data)
{
mData = data;
}
#region IDataObject Members
public object GetData(Type format)
{
return mData;
}
#endregion
}
Related
I am making a game. The movement of the player is a series of pre-defined stages. Because this principle is the backbone of my game and there are many NPCs at once, I try to make this as efficient as possible.
So I want to include the values of each stage by using constants.
At certain events (like a pressed button for the player) the stage switches to another stage. Because I can't refer to another nested class in a constant, I additionally want to set up an array that contains references to each stage.
The names of the classes are written in enums, and I refer by their integer value.
How do I do that? I want to include the solution at the given position in the code.
If there is a better way to do that by the way, I appreciate if you forward me to it.
enum TYPE
{
hum //and more
}
enum HUM
{
standF, stand_jumpF, stand_kneeF, stand_wanderF //and more
}
static class CONST
{
public static class hum
{
public static class standF
{
public const int duration = 1;
public const int actionLeft = (int)HUM.stand_jumpF;
public const int actionRight = (int)HUM.stand_kneeF;
public const int actionFaster = (int)HUM.stand_wanderF;
}
//and more
}
//and more
}
public static class REF
{
public static Type[][] CONST { get; }
static REF()
{
Type[][] REF = new Type[Enum.GetNames(typeof(TYPE)).Length][];
Type[TYPE.hum][] REF = new Type[Enum.GetNames(typeof(HUM)).Length];
for (int i = 0; i < Enum.GetNames(typeof(HUM)).Length; i++)
{
Type[TYPE.hum][i] = //what to do here?
}
}
}
After reading comparable questions about non-nested class I would have used this solution:
Type[TYPE.hum][i] = Type.GetType(Enum.GetName(typeof(HUM), i));
But it doesn't seem to do the trick.
You are messing up with array notations. Have a look at following question for more information: Multidimensional Array [][] vs [,]
I would suggest working with a dictionary (although I find your whole design weird to say the least), you'll need to add using System.Collections.Generic; to your usings.
public static class REF
{
public static Type[][] CONST { get; }
static REF()
{
Dictionary<Enum, List<Enum>> REF = new Dictionary<Enum, List<Enum>>();
REF.Add(TYPE.hum, new List<Enum>());
foreach (var item in Enum.GetValues(typeof(HUM)))
{
REF[TYPE.hum].Add((HUM)item);
}
// Demo code, print all values in TYPE.hum
foreach (var item in REF[TYPE.hum])
{
Console.WriteLine(item);
}
}
}
In the code below, what is the significance of the ref in the GetAge() method signature?
public class Person
{
private int age;
public ref int GetAge()
{
return ref this.age;
}
}
The ref return is a new feature in C# 7.0. It allows to return a reference to a memory position. This was not possible in previous C# versions. You can even store the returned memory location like this:
var person = new Person();
// Here we can store the reference to the memory area and we can modify it
ref int age = ref person.GetAge();
// like this
age = 50;
The whole time we were working on the same memory location and not a copy of the age.
What is happening behind the scenes?
If we have this code:
public class Program
{
public static void Main()
{
var person = new Person();
// Here we can store the reference to the memory area and we can modify it
ref int age = ref person.GetAge();
// like this
age = 50;
}
}
public class Person
{
private int age;
public ref int GetAge()
{
return ref this.age;
}
}
Here is what the compiler (Roslyn) does behind the scenes for that code:
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
[assembly: AssemblyVersion("0.0.0.0")]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[module: UnverifiableCode]
public class Program
{
public unsafe static void Main()
{
Person person = new Person();
int* age = person.GetAge();
*age = 50;
}
}
public class Person
{
private int age;
public unsafe int* GetAge()
{
return ref this.age;
}
}
OK!!! I think we are all glad we do not have to deal with all those * shenanigans.
When is this feature useful?
The addition of ref locals and ref returns enable algorithms that are more efficient by avoiding copying values, or performing dereferencing operations multiple times.
It is most useful when you are working with large data structures which are value types (struct) and passing the copies in and out of methods may not be very effective. For example, imagine we have a class which contains a bunch of struct objects:
class Container
{
private Tile[] tiles = new Tile[] { new Tile { X = 10 } };
public Tile this[int x]
{
get { return tiles[x]; }
set { tiles[x] = value; }
}
}
public struct Tile
{
public int X { get; set; }
// Many more propeties
}
If we wanted to work with the Tile objects, since they are struct, we would not be able to do this:
var container = new Container();
container[0].X = 10;
We cannot do that because the compiler will issue this error:
Error CS1612 Cannot modify the return value of 'Container.this[int]' because it is not a variable
The compiler is throwing that error to be explicitly clear that what you think you are doing (modifying the indexed item), is not really what you are doing. You are actually modifying the copy so it forces you to do exactly that. So, to be able to set the X, you will need to do it on the copy like this:
var container = new Container();
var copy = container[0];
copy.X = 10;
// now we need to set the item to the copy
container[0] = copy;
As you can see that is not very efficient, especially if we are working with a large struct and we need to manipulate many of them in an iterative way.
With C# 7.0, we can do this:
public ref Tile this[int x]
{
get { return ref tiles[x]; }
}
and now we can manipulate the Tiles directly without having to send the copy, making the copy, and then setting the original item to the copy. Like this:
var container = new Container();
ref Tile tile = ref container[0];
tile.X = 10;
A Small Gotcha
There are many examples online and they have the syntax like this:
// Notice the ref missing on the right side
ref int age = person.GetAge();
That will result in this error:
Cannot initialize a by-reference variable with a value
The correct syntax is to have the ref on both sides like this:
ref int age = ref person.GetAge();
More Info
Here is an SO question wherein this feature has been discussed. I guess that question is now history. And here is another article by Eric Lippert about this feature.
Dearest fellow programmers,
I seem to lack some understanding as of how the referencing works in C#.
The case:
I tried to implement some sort of Memento proxy which would wrap an interface and store every parameter that we're provided to the method calls and store these into a list.
Whenever necessary we could call the RestoreState and the objects would "reset" to the original state.
The code:
Consumer and model object
class Program
{
static void Main(string[] args)
{
IMemento memento = new Memento();
PrestationInfo prestationInfo2 = new PrestationInfo { Advance = 2 };
memento.Add(prestationInfo2);
Console.WriteLine(prestationInfo2.Advance); //Expect 2
prestationInfo2.Advance = 1;
Console.WriteLine(prestationInfo2.Advance); //Expect 1
memento.RestoreState();
Console.WriteLine(prestationInfo2.Advance); //Expect 2, but still 1
Console.ReadKey();
}
}
[Serializable]
public class PrestationInfo
{
public int Advance { get; set; }
}
Memento
public interface IMemento
{
void Add(object pItem);
void RestoreState();
}
public class Memento : IMemento
{
public Memento()
{
MementoList = new Dictionary<long, object>();
ReferenceList = new List<object>();
ObjectIDGenerator = new ObjectIDGenerator();
}
private ObjectIDGenerator ObjectIDGenerator { get; set; }
private Dictionary<long, object> MementoList { get; set; }
private List<object> ReferenceList { get; set; }
public void Add(object pItem)
{
bool firstTime;
long id = ObjectIDGenerator.GetId(pItem, out firstTime);
if (firstTime)
{
var mementoObject = DeepCopy(pItem);
MementoList.Add(id, mementoObject);
ReferenceList.Add(pItem);
}
}
public void RestoreState()
{
for (int i = 0; i < ReferenceList.Count; i++)
{
object reference = ReferenceList[i];
bool firstTime;
long id = ObjectIDGenerator.GetId(reference, out firstTime);
if (MementoList.ContainsKey(id))
{
object mementoObject = MementoList[id];
reference = mementoObject;
//reference = PropertyCopy<PrestationInfo>.CopyFrom(mementoObject as PrestationInfo); //Property copy
//Interlocked.Exchange(ref reference, mementoObject); //Also tried this
}
}
}
private static TCopy DeepCopy<TCopy>(TCopy pObjectToCopy)
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, pObjectToCopy);
memoryStream.Position = 0;
return (TCopy)binaryFormatter.Deserialize(memoryStream);
}
}
}
Extra info
My guess is, I'm doing/understand something wrong regarding the List.
I also tried the Interlocked.Exchange, playing around with "ref"'s, using WeakReference's and storing the object into a CareTaker object (and storing that CareTaker into the List), implement some copy Property thing...
And ... I just can't see it.
My expected result would be the PrestationInfo.Advance property containing the value 2. But it keeps
Try this:
Change the Add method:
public long Add(object pItem)
{
bool firstTime;
long id = ObjectIDGenerator.GetId(pItem, out firstTime);
if (firstTime)
{
var mementoObject = DeepCopy(pItem);
MementoList.Add(id, mementoObject);
ReferenceList.Add(pItem);
}
return id; // i need my memento! LOL
}
You should also add this accessor method:
public object GetRestoredState(long id)
{
return MementoList[id]; // you should put some range check here
}
Now that you have your id, you can fetch the restored state this way:
memento.RestoreState();
prestationInfo2 = memento.GetRestoredState(savedId); // <-- you got this when you called the Add()...
Console.WriteLine(prestationInfo2.Advance); //Expect 2, but still 1
Follow ups: you can also make the IMemento into a IMemento<T>, and adjust your code accordingly
Memento.Add needs a ref parameter modifier to access the original reference type pointer.
https://msdn.microsoft.com/en-us/library/14akc2c7.aspx?f=255&MSPPError=-2147217396
It looks like the problem is in your understanding of references in .NET
public void RestoreState()
{
for (int i = 0; i < ReferenceList.Count; i++)
{
object reference = ReferenceList[i];
bool firstTime;
long id = ObjectIDGenerator.GetId(reference, out firstTime);
if (MementoList.ContainsKey(id))
{
object mementoObject = MementoList[id];
reference = mementoObject;
//reference = PropertyCopy<PrestationInfo>.CopyFrom(mementoObject as PrestationInfo); //Property copy
//Interlocked.Exchange(ref reference, mementoObject); //Also tried this
}
}
}
The RestoreState method above does not return anything, and you're strictly operating on references, not their internal state. Inside your method object reference is a local reference. It is not the same as the external prestationInfo2 and your method simply makes reference point (refer) to the previously saved copy of the state of presentationInfo2.
You could modify it something like this:
public object RestoreState()
{
for (int i = 0; i < ReferenceList.Count; i++)
{
object reference = ReferenceList[i];
bool firstTime;
long id = ObjectIDGenerator.GetId(reference, out firstTime);
if (MementoList.ContainsKey(id))
{
object mementoObject = MementoList[id];
reference = mementoObject;
return reference;
}
}
return null;
}
And then call it like this:
presentationInfo2 = memento.RestoreState();
If you want the memento to track objects and magically restore their state you will have to make the objects themselves aware of the memento which introduces coupling or use reflection to modify the tracked references internal state. Basically you don't deserialize the persisted state into a new object but use reflection to overwrite the previously stored internal state into the tracked object reference.
Be careful though to store references using WeakReference otherwise you will find yourself with a nice case of memory leak.
I'm in a situation where I have a ASP.NET Web API 2 project hosted on IIS and I'm anticipating concurrency issues. I need to generate random numbers and make sure that they are unique (later stored in the database). To do so, I have implemented a simple in-memory RNG that rely on a static ConcurrentBag. I am aware that this implementation would imply risks on a distributed architecture. Rapidly, the code looks like this :
public interface IRandomNumberGenerator
{
string ReserveNumber();
string ReleaseNumber(string number);
}
public class InMemoryRandomNumberGenerator : IRandomNumberGenerator
{
private static readonly ConcurrentBag<string> Bag = new ConcurrentBag<string>();
public string ReserveNumber()
{
// Add
throw new NotImplementedException();
}
public string ReleaseNumber(string number)
{
// Remove
throw new NotImplementedException();
}
}
This code is intended to be used like so :
var number = rng.ReserveNumber();
StoreIntoDatabase(number);
rng.ReleaseNumber(number);
Am I using the ConcurrentBag collection appropriately?
Also note that I have simplified my example and that I am not interested in moving code into SQL and use SQL transaction to accomplish this task.
I guess you trying to solve a concurrency problem where many users click a button to generate a number. While ConcurrentBag might be OK to use from the concurrency perspective I see other problems:
"Bags are useful for storing objects when ordering doesn't matter, and unlike sets, bags support duplicates. " msdn. I think you were trying to avoid duplicates.
You need to have some sort of a protected section or a transaction for this sequence, otherwise concurrency issue may appear
var number = rng.ReserveNumber();
StoreIntoDatabase(number);
rng.ReleaseNumber(number);
I hope you don't roll out your own RNG, but reuse something like RNGCryptoServiceProvider.
I have revised the design. I switched to ConcurrentDictionary to avoid duplicates as pointed out by #oleksii. I use a byte because I don't use the value and there is no ConcurrentHashset to my knowledge.
NUnit test :
[Test]
public void GenerateStrings()
{
var gen1 = new ConcurrentStringGenerator("0123456789", 9);
for (int i = 0; i < 100; i++)
{
var str = gen1.Reserve();
Console.WriteLine(int.Parse(str).ToString("000-000-000"));
Assert.True(gen1.Release(str));
}
var gen2 = new ConcurrentStringGenerator("ABCDEFGHJKLMNPQRSTUVWXYZ", 3);
for (int i = 0; i < 100; i++)
{
var str = gen2.Reserve();
Console.WriteLine(str);
Assert.True(gen2.Release(str));
}
}
Implementation :
public class ConcurrentStringGenerator
{
private readonly Random _random;
private readonly string _charset;
private readonly int _length;
private readonly ConcurrentDictionary<string, byte> _numbers;
public ConcurrentStringGenerator(string charset, int length)
{
_charset = charset;
_length = length;
_random = new Random();
_numbers = new ConcurrentDictionary<string, byte>();
}
public string Reserve()
{
var str = Generate();
while (!_numbers.TryAdd(str, 0))
{
str = Generate();
}
return str;
}
public bool Release(string str)
{
byte b;
return _numbers.TryRemove(str, out b);
}
private string Generate()
{
return new string(Enumerable.Repeat(_charset, _length).Select(s => s[_random.Next(s.Length)]).ToArray());
}
}
#oleksii as for the protected section, I'm trying to avoid a lock statement over the sequence and use a concurrent collection instead. Can you be more specific on the following statement?
You need to have some sort of a protected section or a transaction for
this sequence, otherwise concurrency issue may appear
In an application of mine, I need a large constant (actually static readonly) array of objects. The array is initialized in the type's static constructor.
The array contains more than a thousand items, and when the type is first used, my program experiences a serious slowdown. I would like to know if there is a way to initialise a large array quickly in C#.
public static class XSampa {
public class XSampaPair : IComparable<XSampaPair> {
public XSampaPair GetReverse() {
return new XSampaPair(Key, Target);
}
public string Key { get; private set; }
public string Target { get; private set; }
internal XSampaPair(string key, string target) {
Key = key;
Target = target;
}
public int CompareTo(XSampaPair other) {
if (other == null)
throw new ArgumentNullException("other",
"Cannot compare with Null.");
if (Key == null)
throw new NullReferenceException("Key is null!");
if (other.Key == null)
throw new NullReferenceException("Key is null!");
if (Key.Length == other.Key.Length)
return string.Compare(Key, other.Key,
StringComparison.InvariantCulture);
return other.Key.Length - other.Key;
}
}
private static readonly XSampaPair[] pairs, reversedPairs;
public static string ParseXSampaToIpa(this string xsampa) {
// Parsing code here...
}
public static string ParseIpaToXSampa(this string ipa) {
// reverse code here...
}
static XSampa() {
pairs = new [] {
new XSampaPair("a", "\u0061"),
new XSampaPair("b", "\u0062"),
new XSampaPair("b_<", "\u0253"),
new XSampaPair("c", "\u0063"),
// And many more pairs initialized here...
};
var temp = pairs.Select(x => x.GetReversed());
reversedPairs = temp.ToArray();
Array.Sort(pairs);
Array.Sort(reversedPairs);
}
}
PS: I use to array to convert X-SAMPA phonetic transcription to a Unicode string with the corresponding IPA characters.
You can serialize a completely initialized onject into a binary file, add that file as a resource, and load it into your array on startup. If your constructors are CPU-intensive, you might get an improvement. Since your code appears to perform some sort of parsing, the chances of getting a decent improvement there are fairly high.
You could use an IEnumerable<yourobj> which would let you lazily yield return the enumerable only as needed.
The problem with this is you won't be able to index into it like you could using the array.