5
\$\begingroup\$

I apologize if this isn't fit for this site but it is a puzzle, not all that practical, so I thought it would fit.

Take this code:

string s = "";
Console.WriteLine(ReferenceEquals(s, string.Empty));

This will print out true because "" is interned by the JITter at some early point and literals are reused from the interning cache.

The question and puzzle is this:

How can you initialize s such that
1. s.Length == 0
2. ReferenceEquals(s, string.Empty) == false

I've tried the following:

string s = "test".Substring(0, 0); // any arbitrary legal start offset tried
string s = new string(' ', 0);
string s = new string(new char[0]);
string s = CallSomeMethodThatWontBeInlined() + CallSomeMethodThatWontBeInlined();

These three all end up being equal to string.Empty.

Bonus if you can explain the above behavior with regards to new string(...). I have my thoughts about it but no solid evidence one way or another.

I can understand that all methods that concatenate or take substrings of existing strings special-case the result that will end up with length zero, but how new string(...) actually doesn't create a new object I don't know.

Note! I'm asking for legal C#. I'm not asking for a p/invoke or assembly code call that just manually constructs the bytes of an empty string object in memory and stuffs a pointer to it into a string variable.

Any method or path through existing classes in .NET or using operators of strings or whatnot that would end up with the required results are legal. As such, take the Substring example above, if that returned a new empty string that isn't the same as the existing one, then that would be a/the answer to this puzzle.

Reflection is boundary, at the very least it has to be reflection that can run in untrusted scenarios.


My own "conclusion" is that this may indeed be impossible because the actual C# code that would end up constructing such a string would end up with one of the new string(...) overloads, or using one of the operators which all seem to handle this cases. As such, the end-point of any such code may be the one that handles this case and since it does, it may not be possible.

\$\endgroup\$

closed as off-topic by Mego, Kritixi Lithos, Denker, TuxCrafting, Timtech Nov 10 '16 at 12:08

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "This site is for programming contests and challenges. General programming questions are off-topic here. You may be able to get help on Stack Overflow." – Mego, Kritixi Lithos, TuxCrafting
If this question can be reworded to fit the rules in the help center, please edit the question.

  • \$\begingroup\$ I guess without some IL this might be impossible. You should also say drop the legal .NET code, because IL is .NET. Otherwise I'd use C++/CLI or any other .NET language. If you only want C#, say C# \$\endgroup\$ – Karl Napf Nov 10 '16 at 11:15
  • \$\begingroup\$ OK, I dropped the bit about .NET. \$\endgroup\$ – Lasse Vågsæther Karlsen Nov 10 '16 at 11:16
  • \$\begingroup\$ For your own research, you can try to disassemble the .NET library and look at the source of the string class. \$\endgroup\$ – Karl Napf Nov 10 '16 at 11:20
  • \$\begingroup\$ .NET is now largely open sourced on github, the string C# source is here - github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/… - and the native methods are here - github.com/dotnet/coreclr/blob/master/src/classlibnative/… - but most of the native code is greek to me :) \$\endgroup\$ – Lasse Vågsæther Karlsen Nov 10 '16 at 11:31
  • \$\begingroup\$ In my opinion if you disallow reflection as well you're pretty much asking people to find a bug. \$\endgroup\$ – ASCII-only Nov 10 '16 at 11:51
3
\$\begingroup\$

So, I'll take the easy way:

string s = "";

So, s.Length is now 0, so far so good. But how can we get ReferenceEquals(s, string.Empty) to false.

Piece of cake, just put that into your class:

public static new bool ReferenceEquals(object a, object b) => false;

and the above call will result in false.

I will look for some more sneaky methods, so.

\$\endgroup\$
2
\$\begingroup\$

1st try: Reflection

String s = "abc";
PropertyInfo pi = s.GetType().GetProperty("Length");
pi.SetValue(s, 0, null);

Yields error, there is no set property

Loophole

var s = new { Length = 0 };

Now the ReferenceEquals will return false and s.Length == 0. Just sticking to your rules =)

\$\endgroup\$
  • \$\begingroup\$ Another devious circumvention :) \$\endgroup\$ – Lasse Vågsæther Karlsen Nov 10 '16 at 12:12
  • \$\begingroup\$ The question asked how to initialise s, but it didn't say that you could also change the type of s. This is either answering a completely different question or interpreting the challenge too literally, which is against the site rules. \$\endgroup\$ – Peter Taylor Nov 10 '16 at 14:46

Not the answer you're looking for? Browse other questions tagged or ask your own question.