I use Python.Net for C# interaction with Python libraries. I solve the problem of text classification. I use FastText to index and get the vector, as well as Sklearn to train the classifier (Knn).During the implementation, I encountered a lot of problems, but all were solved, with the exception of one.
After receiving the vectors of the texts on which I train Knn, I save them to a separate text file and then, if necessary, use it.
string loadKnowVec = File.ReadAllText("vectorKnowClass.txt", Encoding.Default);
string[] splitKnowVec = loadKnowVec.Split('\r');
splitKnowVec = splitKnowVec.Where(x => x != "").ToArray();
for()
{
keyValues_vector.Add(float.Parse(splitKnowVec[i], NumberFormatInfo.InvariantInfo), 1);
}
dynamic X_vec = np.array(keyValues_vector.Keys.ToArray()).reshape(-1, 1);
dynamic y_tag = np.array(keyValues_vector.Values.ToArray());
dynamic neigh = KNN(n_neighbors: 3);
dynamic KnnFit = neigh.fit(X_vec, y_tag);
string predict = neigh.predict("0.00889");
MessageBox.Show("Скорее всего это: "+predict);
During the training of the classifier, I encountered such a problem that from c# to python, it is not values with the float type, but the value of System.Single[].
Python.Runtime.PythonException: "TypeError : float() argument must be a string or a number,
not 'Single[]'
The stored value, at this point, of dynamic X_vec is "System.Single[]".(I think that's exactly the problem)
2.At first I tried to manually set the values of X_vec, but the error and its values were the same.
The first idea was to change the array type using the numpy library, but it didn't help, it also gave out "".
dynamic Xx = np.array(X_vec, dtype: "float");
dynamic yY = np.array(y_tag, dtype: "int");
Next, it was tried to create an empty array in advance and load specific values into it before changing the data type, but this also did not work.
Perhaps I do not understand the principle of the formation and interaction of the MSVS19 IDE and the python interpreter.
I solved this issue for a couple of days and each time I thought it was worth reading the documentation on python.net .
As a result, I found a solution and it turned out to be quite banal, it is necessary to represent X_vec not as a float[] , but as a List<float>
List<float> vectors = keyValues_vector.Keys.ToList();
List<int> classTag = keyValues_vector.Values.ToList();
dynamic a = np.array(vectors);
dynamic X_vec = a.reshape(-1, 1);
dynamic y_tag = np.array(classTag);
I was able to create an array of a struct I created, but I'm having trouble doing the same for an array of a class. I'm (faintly) aware that this probably isn't the best way to do this, but I'd appreciate help in figuring out what's going on.
I'm about 2 days into learning C#, and I'm navigating away from MS Office-VBA, if that gives you an idea of what I'm into. Anyway, I'm following an online reference, and along the way trying to play with what I've learned so far. This problem has come about as a result of my playing.
First, let me describe what I've done with the struct, and the array of that struct, with some code snippets.
I've been able to create a struct, called Machines...
// play with structs
struct Machines
{
// vars for struct
private string model, SN;
private int hours;
// assign values
public void AssignValues(string model_in, string SN_in, int hours_in)
{
model = model_in;
SN = SN_in;
hours = hours_in;
}
// display values
public void DisplayValues()
{
Console.WriteLine("Model: {0}", model);
Console.WriteLine("SN: {0}", SN);
Console.WriteLine("Hours: {0}", hours);
}
};
... things seem to work just fine:
public static void Main()
{
// play with structures
Machines machine1 = new Machines();
machine1.AssignValues("AA", "ABC01234", 34760);
machine1.DisplayValues();
Output is:
Model: AA
SN: ABC01234
Hours: 34760
Then, I can create an array of the struct, and things continue to go well:
// play with structures and arrays
// declare, create new instance
Machines [] MyArr = new Machines[10];
MyArr[0].AssignValues("AA", "ABC01235", 43000);
MyArr[0].DisplayValues();
But, when I attempt to do the same with a class, it's a different story. What's going on?
public class ArmstrongMachine
{
// vars for struct
private string model, SN;
private int hours;
// assign values
public void AssignValues(string model_in, string SN_in, int hours_in)
{
model = model_in;
SN = SN_in;
hours = hours_in;
}
// display values
public void DisplayValues()
{
Console.WriteLine("Model: {0}", model);
Console.WriteLine("SN: {0}", SN);
Console.WriteLine("Hours: {0}", hours);
}
};
...
// play with classes
ArmstrongMachine [] MyMachines = new ArmstrongMachine[10];
MyMachines[0].AssignValues("AA", "ABC01236", 51000);
MyMachines[0].DisplayValues();
The issue seems to begin with MyMachines[0].AssignValues.... If I comment out that line and the following, there are no problems (other than the warning that I've created a variable I'm not using).
Any ideas?
Also, please be aware that this is being compiled online.
A class gives you a reference type in C#.
The array thus holds references to objects, and not the objects themselves.
The array initially contains nothing, all zeroes, which means all the references will be null.
You need to initialize each element of the array to hold an object reference:
// play with classes
ArmstrongMachine [] MyMachines = new ArmstrongMachine[10];
MyMachines[0] = new ArmstrongMachine();
MyMachines[0].AssignValues("AA", "ABC01236", 51000);
MyMachines[0].DisplayValues();
MyMachines[1] = new ArmstrongMachine();
MyMachines[2] = new ArmstrongMachine();
...
MyMachines[9] = new ArmstrongMachine();
If the array holds value types, like the structs, then the array holds the struct values themselves, thus it works with the structs, and not with the objects.
Also note that you should emphatically not use mutable structs (structs you can change). There's tons of things that can go wrong and bite you in ... so you should not use them. Go with classes in this case.
Here's a video on the subject of mutable structs: Evil Structs, by Jon Skeet.
In simple words: A struct is only a way the memory is structured whereas a class is a real object. For the second example, you need to create instances of the class before you can make calls to it, because it only points to a memory location (which may be unassigned = a null reference):
ArmstrongMachine [] MyMachines = new ArmstrongMachine[10];
MyMachines[0] = new ArmstrongMachine();
MyMachines[0].AssignValues("AA", "ABC01236", 51000);
MyMachines[0].DisplayValues();
I have a problem using a class of made of structures.
Here's the basic definition:
using System;
struct Real
{
public double real;
public Real(double real)
{
this.real = real;
}
}
class Record
{
public Real r;
public Record(double r)
{
this.r = new Real(r);
}
public void Test(double origval, double newval)
{
if (this.r.real == newval)
Console.WriteLine("r = newval-test passed\n");
else if (this.r.real == origval)
Console.WriteLine("r = origval-test failed\n");
else
Console.WriteLine("r = neither-test failed\n");
}
}
When I create a non-dynamic (static?) Record, setting the Real works.
When I create a dynamic Record, setting the real doesn't work.
When I create a dynamic Record, replacing the real works.
And here's the test program
class Program
{
static void Main(string[] args)
{
double origval = 8.0;
double newval = 5.0;
// THIS WORKS - create fixed type Record, print, change value, print
Record record1 = new Record(origval);
record1.r.real = newval; // change value ***
record1.Test(origval, newval);
// THIS DOESN'T WORK. change value is not making any change!
dynamic dynrecord2 = new Record(origval);
dynrecord2.r.real = newval; // change value
dynrecord2.Test(origval, newval);
// THIS WORKS - create dynamic type Record, print, change value, print
dynamic dynrecord3 = new Record(origval);
dynamic r = dynrecord3.r; // copy out value
r.real = newval; // change copy
dynrecord3.r = r; // copy in modified value
dynrecord3.Test(origval, newval);
}
}
And here's the output:
r = newval-test passed
r = origval-test failed
r = newval-test passed
When I change the struct Real to class Real, all three cases work.
So what's going on?
Thanks,
Max
dynamic is really a fancy word for object as far as the core CLI is concerned, so you are mutating a boxed copy. This is prone to craziness. Mutating a struct in the first place is really, really prone to error. I would simply make the struct immutable - otherwise you are going to get this over and over.
I dug a little deeper into this problem. Here's an answer from Mads Torgersen of Microsoft.
From Mads:
This is a little unfortunate but by design. In
dynrecord2.r.real = newval; // change value
The value of dynrecord2.r gets boxed, which means copied into its own heap object. That copy is the one getting modified, not the original that you subsequently test.
This is a consequence of the very “local” way in which C# dynamic works. Think about a statement like the above – there are two fundamental ways that we could attack that:
1) Realize at compile time that something dynamic is going on, and essentially move the whole statement to be bound at runtime
2) Bind individual operations at runtime when their constituents are dynamic, returning something dynamic that may in turn cause things to be bound at runtime
In C# we went with the latter, which is nicely compositional, and makes it easy to describe dynamic in terms of the type system, but has some drawbacks – such as boxing of resulting value types for instance.
So what you are seeing is a result of this design choice.
I took another look at the MSIL. It essentially takes
dynrecord2.r.real = newval;
and turns it into:
Real temp = dynrecord2.r;
temp.real = newval;
If dynrecord2.r is a class, it just copies the handle so the change affects the internal field. If dynrecord2.r is a struct, a copy is made, and the change doesn't affect the original.
I'll leave it up to the reader to decide if this is a bug or a feature.
Max
Make your struct immutable and you won't have problems.
struct Real
{
private double real;
public double Real{get{return real;}}
public Real(double real)
{
this.real = real;
}
}
Mutable structs can be useful in native interop or some high performance scenarios, but then you better know what you're doing.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I was recently working with a DateTime object, and wrote something like this:
DateTime dt = DateTime.Now;
dt.AddDays(1);
return dt; // still today's date! WTF?
The intellisense documentation for AddDays() says it adds a day to the date, which it doesn't - it actually returns a date with a day added to it, so you have to write it like:
DateTime dt = DateTime.Now;
dt = dt.AddDays(1);
return dt; // tomorrow's date
This one has bitten me a number of times before, so I thought it would be useful to catalog the worst C# gotchas.
private int myVar;
public int MyVar
{
get { return MyVar; }
}
Blammo. Your app crashes with no stack trace. Happens all the time.
(Notice capital MyVar instead of lowercase myVar in the getter.)
Type.GetType
The one which I've seen bite lots of people is Type.GetType(string). They wonder why it works for types in their own assembly, and some types like System.String, but not System.Windows.Forms.Form. The answer is that it only looks in the current assembly and in mscorlib.
Anonymous methods
C# 2.0 introduced anonymous methods, leading to nasty situations like this:
using System;
using System.Threading;
class Test
{
static void Main()
{
for (int i=0; i < 10; i++)
{
ThreadStart ts = delegate { Console.WriteLine(i); };
new Thread(ts).Start();
}
}
}
What will that print out? Well, it entirely depends on the scheduling. It will print 10 numbers, but it probably won't print 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 which is what you might expect. The problem is that it's the i variable which has been captured, not its value at the point of the creation of the delegate. This can be solved easily with an extra local variable of the right scope:
using System;
using System.Threading;
class Test
{
static void Main()
{
for (int i=0; i < 10; i++)
{
int copy = i;
ThreadStart ts = delegate { Console.WriteLine(copy); };
new Thread(ts).Start();
}
}
}
Deferred execution of iterator blocks
This "poor man's unit test" doesn't pass - why not?
using System;
using System.Collections.Generic;
using System.Diagnostics;
class Test
{
static IEnumerable<char> CapitalLetters(string input)
{
if (input == null)
{
throw new ArgumentNullException(input);
}
foreach (char c in input)
{
yield return char.ToUpper(c);
}
}
static void Main()
{
// Test that null input is handled correctly
try
{
CapitalLetters(null);
Console.WriteLine("An exception should have been thrown!");
}
catch (ArgumentNullException)
{
// Expected
}
}
}
The answer is that the code within the source of the CapitalLetters code doesn't get executed until the iterator's MoveNext() method is first called.
I've got some other oddities on my brainteasers page.
The Heisenberg Watch Window
This can bite you badly if you're doing load-on-demand stuff, like this:
private MyClass _myObj;
public MyClass MyObj {
get {
if (_myObj == null)
_myObj = CreateMyObj(); // some other code to create my object
return _myObj;
}
}
Now let's say you have some code elsewhere using this:
// blah
// blah
MyObj.DoStuff(); // Line 3
// blah
Now you want to debug your CreateMyObj() method. So you put a breakpoint on Line 3 above, with intention to step into the code. Just for good measure, you also put a breakpoint on the line above that says _myObj = CreateMyObj();, and even a breakpoint inside CreateMyObj() itself.
The code hits your breakpoint on Line 3. You step into the code. You expect to enter the conditional code, because _myObj is obviously null, right? Uh... so... why did it skip the condition and go straight to return _myObj?! You hover your mouse over _myObj... and indeed, it does have a value! How did THAT happen?!
The answer is that your IDE caused it to get a value, because you have a "watch" window open - especially the "Autos" watch window, which displays the values of all variables/properties relevant to the current or previous line of execution. When you hit your breakpoint on Line 3, the watch window decided that you would be interested to know the value of MyObj - so behind the scenes, ignoring any of your breakpoints, it went and calculated the value of MyObj for you - including the call to CreateMyObj() that sets the value of _myObj!
That's why I call this the Heisenberg Watch Window - you cannot observe the value without affecting it... :)
GOTCHA!
Edit - I feel #ChristianHayter's comment deserves inclusion in the main answer, because it looks like an effective workaround for this issue. So anytime you have a lazy-loaded property...
Decorate your property with [DebuggerBrowsable(DebuggerBrowsableState.Never)] or [DebuggerDisplay("<loaded on demand>")]. – Christian Hayter
Re-throwing exceptions
A gotcha that gets lots of new developers, is the re-throw exception semantics.
Lots of time I see code like the following
catch(Exception e)
{
// Do stuff
throw e;
}
The problem is that it wipes the stack trace and makes diagnosing issues much harder, cause you can not track where the exception originated.
The correct code is either the throw statement with no args:
catch(Exception)
{
throw;
}
Or wrapping the exception in another one, and using inner exception to get the original stack trace:
catch(Exception e)
{
// Do stuff
throw new MySpecialException(e);
}
Here's another time one that gets me:
static void PrintHowLong(DateTime a, DateTime b)
{
TimeSpan span = a - b;
Console.WriteLine(span.Seconds); // WRONG!
Console.WriteLine(span.TotalSeconds); // RIGHT!
}
TimeSpan.Seconds is the seconds portion of the timespan (2 minutes and 0 seconds has a seconds value of 0).
TimeSpan.TotalSeconds is the entire timespan measured in seconds (2 minutes has a total seconds value of 120).
Leaking memory because you didn't un-hook events.
This even caught out some senior developers I know.
Imagine a WPF form with lots of things in it, and somewhere in there you subscribe to an event. If you don't unsubscribe then the entire form is kept around in memory after being closed and de-referenced.
I believe the issue I saw was creating a DispatchTimer in the WPF form and subscribing to the Tick event, if you don't do a -= on the timer your form leaks memory!
In this example your teardown code should have
timer.Tick -= TimerTickEventHandler;
This one is especially tricky since you created the instance of the DispatchTimer inside the WPF form, so you would think that it would be an internal reference handled by the Garbage Collection process... unfortunately the DispatchTimer uses a static internal list of subscriptions and services requests on the UI thread, so the reference is 'owned' by the static class.
Maybe not really a gotcha because the behavior is written clearly in MSDN, but has broken my neck once because I found it rather counter-intuitive:
Image image = System.Drawing.Image.FromFile("nice.pic");
This guy leaves the "nice.pic" file locked until the image is disposed. At the time I faced it I though it would be nice to load icons on the fly and didn't realize (at first) that I ended up with dozens of open and locked files! Image keeps track of where it had loaded the file from...
How to solve this? I thought a one liner would do the job. I expected an extra parameter for FromFile(), but had none, so I wrote this...
using (Stream fs = new FileStream("nice.pic", FileMode.Open, FileAccess.Read))
{
image = System.Drawing.Image.FromStream(fs);
}
If you count ASP.NET, I'd say the webforms lifecycle is a pretty big gotcha to me. I've spent countless hours debugging poorly written webforms code, just because a lot of developers just don't really understand when to use which event handler (me included, sadly).
overloaded == operators and untyped containers (arraylists, datasets, etc.):
string my = "my ";
Debug.Assert(my+"string" == "my string"); //true
var a = new ArrayList();
a.Add(my+"string");
a.Add("my string");
// uses ==(object) instead of ==(string)
Debug.Assert(a[1] == "my string"); // true, due to interning magic
Debug.Assert(a[0] == "my string"); // false
Solutions?
always use string.Equals(a, b) when you are comparing string types
using generics like List<string> to ensure that both operands are strings.
[Serializable]
class Hello
{
readonly object accountsLock = new object();
}
//Do stuff to deserialize Hello with BinaryFormatter
//and now... accountsLock == null ;)
Moral of the story : Field initialisers are not run when deserializing an object
DateTime.ToString("dd/MM/yyyy"); This will actually not always give you dd/MM/yyyy but instead it will take into account the regional settings and replace your date separator depending on where you are. So you might get dd-MM-yyyy or something alike.
The right way to do this is to use DateTime.ToString("dd'/'MM'/'yyyy");
DateTime.ToString("r") is supposed to convert to RFC1123, which uses GMT. GMT is within a fraction of a second from UTC, and yet the "r" format specifier does not convert to UTC, even if the DateTime in question is specified as Local.
This results in the following gotcha (varies depending on how far your local time is from UTC):
DateTime.Parse("Tue, 06 Sep 2011 16:35:12 GMT").ToString("r")
> "Tue, 06 Sep 2011 17:35:12 GMT"
Whoops!
I saw this one posted the other day, and I think it is pretty obscure, and painful for those that don't know
int x = 0;
x = x++;
return x;
As that will return 0 and not 1 as most would expect
I'm a bit late to this party, but I have two gotchas that have both bitten me recently:
DateTime resolution
The Ticks property measures time in 10-millionths of a second (100 nanosecond blocks), however the resolution is not 100 nanoseconds, it's about 15ms.
This code:
long now = DateTime.Now.Ticks;
for (int i = 0; i < 10; i++)
{
System.Threading.Thread.Sleep(1);
Console.WriteLine(DateTime.Now.Ticks - now);
}
will give you an output of (for example):
0
0
0
0
0
0
0
156254
156254
156254
Similarly, if you look at DateTime.Now.Millisecond, you'll get values in rounded chunks of 15.625ms: 15, 31, 46, etc.
This particular behaviour varies from system to system, but there are other resolution-related gotchas in this date/time API.
Path.Combine
A great way to combine file paths, but it doesn't always behave the way you'd expect.
If the second parameter starts with a \ character, it won't give you a complete path:
This code:
string prefix1 = "C:\\MyFolder\\MySubFolder";
string prefix2 = "C:\\MyFolder\\MySubFolder\\";
string suffix1 = "log\\";
string suffix2 = "\\log\\";
Console.WriteLine(Path.Combine(prefix1, suffix1));
Console.WriteLine(Path.Combine(prefix1, suffix2));
Console.WriteLine(Path.Combine(prefix2, suffix1));
Console.WriteLine(Path.Combine(prefix2, suffix2));
Gives you this output:
C:\MyFolder\MySubFolder\log\
\log\
C:\MyFolder\MySubFolder\log\
\log\
When you start a process (using System.Diagnostics) that writes to the console, but you never read the Console.Out stream, after a certain amount of output your app will appear to hang.
No operator shortcuts in Linq-To-Sql
See here.
In short, inside the conditional clause of a Linq-To-Sql query, you cannot use conditional shortcuts like || and && to avoid null reference exceptions; Linq-To-Sql evaluates both sides of the OR or AND operator even if the first condition obviates the need to evaluate the second condition!
Using default parameters with virtual methods
abstract class Base
{
public virtual void foo(string s = "base") { Console.WriteLine("base " + s); }
}
class Derived : Base
{
public override void foo(string s = "derived") { Console.WriteLine("derived " + s); }
}
...
Base b = new Derived();
b.foo();
Output:
derived base
Value objects in mutable collections
struct Point { ... }
List<Point> mypoints = ...;
mypoints[i].x = 10;
has no effect.
mypoints[i] returns a copy of a Point value object. C# happily lets you modify a field of the copy. Silently doing nothing.
Update:
This appears to be fixed in C# 3.0:
Cannot modify the return value of 'System.Collections.Generic.List<Foo>.this[int]' because it is not a variable
Perhaps not the worst, but some parts of the .net framework use degrees while others use radians (and the documentation that appears with Intellisense never tells you which, you have to visit MSDN to find out)
All of this could have been avoided by having an Angle class instead...
For C/C++ programmers, the transition to C# is a natural one. However, the biggest gotcha I've run into personally (and have seen with others making the same transition) is not fully understanding the difference between classes and structs in C#.
In C++, classes and structs are identical; they only differ in the default visibility, where classes default to private visibility and structs default to public visibility. In C++, this class definition
class A
{
public:
int i;
};
is functionally equivalent to this struct definition.
struct A
{
int i;
};
In C#, however, classes are reference types while structs are value types. This makes a BIG difference in (1) deciding when to use one over the other, (2) testing object equality, (3) performance (e.g., boxing/unboxing), etc.
There is all kinds of information on the web related to the differences between the two (e.g., here). I would highly encourage anyone making the transition to C# to at least have a working knowledge of the differences and their implications.
Garbage collection and Dispose(). Although you don't have to do anything to free up memory, you still have to free up resources via Dispose(). This is an immensely easy thing to forget when you are using WinForms, or tracking objects in any way.
Arrays implement IList
But don't implement it. When you call Add, it tells you that it doesn't work. So why does a class implement an interface when it can't support it?
Compiles, but doesn't work:
IList<int> myList = new int[] { 1, 2, 4 };
myList.Add(5);
We have this issue a lot, because the serializer (WCF) turns all the ILists into arrays and we get runtime errors.
foreach loops variables scope!
var l = new List<Func<string>>();
var strings = new[] { "Lorem" , "ipsum", "dolor", "sit", "amet" };
foreach (var s in strings)
{
l.Add(() => s);
}
foreach (var a in l)
Console.WriteLine(a());
prints five "amet", while the following example works fine
var l = new List<Func<string>>();
var strings = new[] { "Lorem" , "ipsum", "dolor", "sit", "amet" };
foreach (var s in strings)
{
var t = s;
l.Add(() => t);
}
foreach (var a in l)
Console.WriteLine(a());
MS SQL Server can't handle dates before 1753. Significantly, that is out of synch with the .NET DateTime.MinDate constant, which is 1/1/1. So if you try to save a mindate, a malformed date (as recently happened to me in a data import) or simply the birth date of William the Conqueror, you're gonna be in trouble. There is no built-in workaround for this; if you're likely to need to work with dates before 1753, you need to write your own workaround.
The contract on Stream.Read is something that I've seen trip up a lot of people:
// Read 8 bytes and turn them into a ulong
byte[] data = new byte[8];
stream.Read(data, 0, 8); // <-- WRONG!
ulong data = BitConverter.ToUInt64(data);
The reason this is wrong is that Stream.Read will read at most the specified number of bytes, but is entirely free to read just 1 byte, even if another 7 bytes are available before end of stream.
It doesn't help that this looks so similar to Stream.Write, which is guaranteed to have written all the bytes if it returns with no exception. It also doesn't help that the above code works almost all the time. And of course it doesn't help that there is no ready-made, convenient method for reading exactly N bytes correctly.
So, to plug the hole, and increase awareness of this, here is an example of a correct way to do this:
/// <summary>
/// Attempts to fill the buffer with the specified number of bytes from the
/// stream. If there are fewer bytes left in the stream than requested then
/// all available bytes will be read into the buffer.
/// </summary>
/// <param name="stream">Stream to read from.</param>
/// <param name="buffer">Buffer to write the bytes to.</param>
/// <param name="offset">Offset at which to write the first byte read from
/// the stream.</param>
/// <param name="length">Number of bytes to read from the stream.</param>
/// <returns>Number of bytes read from the stream into buffer. This may be
/// less than requested, but only if the stream ended before the
/// required number of bytes were read.</returns>
public static int FillBuffer(this Stream stream,
byte[] buffer, int offset, int length)
{
int totalRead = 0;
while (length > 0)
{
var read = stream.Read(buffer, offset, length);
if (read == 0)
return totalRead;
offset += read;
length -= read;
totalRead += read;
}
return totalRead;
}
/// <summary>
/// Attempts to read the specified number of bytes from the stream. If
/// there are fewer bytes left before the end of the stream, a shorter
/// (possibly empty) array is returned.
/// </summary>
/// <param name="stream">Stream to read from.</param>
/// <param name="length">Number of bytes to read from the stream.</param>
public static byte[] Read(this Stream stream, int length)
{
byte[] buf = new byte[length];
int read = stream.FillBuffer(buf, 0, length);
if (read < length)
Array.Resize(ref buf, read);
return buf;
}
The Nasty Linq Caching Gotcha
See my question that led to this discovery, and the blogger who discovered the problem.
In short, the DataContext keeps a cache of all Linq-to-Sql objects that you have ever loaded. If anyone else makes any changes to a record that you have previously loaded, you will not be able to get the latest data, even if you explicitly reload the record!
This is because of a property called ObjectTrackingEnabled on the DataContext, which by default is true. If you set that property to false, the record will be loaded anew every time... BUT... you can't persist any changes to that record with SubmitChanges().
GOTCHA!
Events
I never understood why events are a language feature. They are complicated to use: you need to check for null before calling, you need to unregister (yourself), you can't find out who is registered (eg: did I register?). Why isn't an event just a class in the library? Basically a specialized List<delegate>?
Today I fixed a bug that eluded for long time. The bug was in a generic class that was used in multi threaded scenario and a static int field was used to provide lock free synchronisation using Interlocked. The bug was caused because each instantiation of the generic class for a type has its own static. So each thread got its own static field and it wasn't used a lock as intended.
class SomeGeneric<T>
{
public static int i = 0;
}
class Test
{
public static void main(string[] args)
{
SomeGeneric<int>.i = 5;
SomeGeneric<string>.i = 10;
Console.WriteLine(SomeGeneric<int>.i);
Console.WriteLine(SomeGeneric<string>.i);
Console.WriteLine(SomeGeneric<int>.i);
}
}
This prints
5
10
5
Just found a weird one that had me stuck in debug for a while:
You can increment null for a nullable int without throwing an excecption and the value stays null.
int? i = null;
i++; // I would have expected an exception but runs fine and stays as null
Enumerables can be evaluated more than once
It'll bite you when you have a lazily-enumerated enumerable and you iterate over it twice and get different results. (or you get the same results but it executes twice unnecessarily)
For example, while writing a certain test, I needed a few temp files to test the logic:
var files = Enumerable.Range(0, 5)
.Select(i => Path.GetTempFileName());
foreach (var file in files)
File.WriteAllText(file, "HELLO WORLD!");
/* ... many lines of codes later ... */
foreach (var file in files)
File.Delete(file);
Imagine my surprise when File.Delete(file) throws FileNotFound!!
What's happening here is that the files enumerable got iterated twice (the results from the first iteration are simply not remembered) and on each new iteration you'd be re-calling Path.GetTempFilename() so you'll get a different set of temp filenames.
The solution is, of course, to eager-enumerate the value by using ToArray() or ToList():
var files = Enumerable.Range(0, 5)
.Select(i => Path.GetTempFileName())
.ToArray();
This is even scarier when you're doing something multi-threaded, like:
foreach (var file in files)
content = content + File.ReadAllText(file);
and you find out content.Length is still 0 after all the writes!! You then begin to rigorously checks that you don't have a race condition when.... after one wasted hour... you figured out it's just that tiny little Enumerable gotcha thing you forgot....
TextInfo textInfo = Thread.CurrentThread.CurrentCulture.TextInfo;
textInfo.ToTitleCase("hello world!"); //Returns "Hello World!"
textInfo.ToTitleCase("hElLo WoRld!"); //Returns "Hello World!"
textInfo.ToTitleCase("Hello World!"); //Returns "Hello World!"
textInfo.ToTitleCase("HELLO WORLD!"); //Returns "HELLO WORLD!"
Yes, this behavior is documented, but that certainly doesn't make it right.