C# has made me sad :(

Pages: 12
*sigh* I've been coding in C# exclusively for the past 1-2 weeks in order to get more familiar with the language. It's mostly been a blast. I'll write code, I'll think of how to make it more expedient, and some magical C# functions will do the trick. The fact that I can do something like this:

1
2
3
4
5
6
7
8
9
10
11
12
static void Main()
    {
        int[] j = Console.ReadLine().Split(' ').Select(int.Parse).ToArray();
        int[] k = Console.ReadLine().Split(' ').Select(int.Parse).ToArray();

        var a = j.Intersect(k).ToArray();

        

        Array.Sort(a);
        Console.WriteLine(String.Join(" ", a));
    }


Is heart soothing.

However, I start to miss C++ a lot when it comes to dynamic arrays...

You'd think it would be the opposite. You'd think C# with it's fahgettaboudit memory handling would make it nicer to use than C++, but it almost just doesn't hold a candle..

Every time an array is created, you have to allocate the memory yourself with new. It's not a pain, but it always make me think, "why?" Now, with regular old arrays, sure, understandable. But the real problem is with Lists.

As soon as you need dynamic memory, you quickly realize how much better C++ vectors are compared to to C# Lists. Vectors are so easy to use. A mentally handicapped cow could use C++ vectors. But C# Lists.. It became ugly. Not only can you not resize a List, but all copies are shallow by default. Both of these caused inconveniences while coding.

There's no resize, so you have to loop through the amount of elements you want the List to hold, adding literally nothing by just allocating empty memory. This is a pain, a little extra coding that feels so easily avoidable.

A little pet peeve is that even though most things you get their size with .Length, Lists are .Count which just makes me wonder why they don't rename it to the same thing.


Anyway, these aren't anything I can't get over after a while, but while coding this specific program I ran into an issue that was so frustrating, because I knew I'd of finished 3 light years ago (yes, I know) had I been coding it in C++!


While coding, I used a List of a List of tuple<int, int> which when outputted produces "( )" around the numbers by default.. Since it was a coding contest, my output had to match. So to get rid of the that extra garbage I had to do this:

1
2
3
4
string q = String.Join("|", b[i]);
q=q.Replace("(", ""); q=q.Replace(")", "");
q = q.Replace(",", ""); q = q.Replace("|", ", ");
Console.WriteLine(q);


The only saving grace was that I can shorten it to:

 
Console.WriteLine(String.Join("|", b[i]).Replace("(", "").Replace(")", "").Replace(",", "").Replace("|", ", "));



I don't know. Each language had it's own advantages and disadvantages in this program which made me sad. Here's the C# and C++ code:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using System;
using System.Linq;
using System.Collections.Generic;

class S
{
    static void Main()
    {
        string S = Console.ReadLine();
        int N = int.Parse(Console.ReadLine()); //Take Integer To Know How Much Input To Take
        List<string> a = new List<string>();
        List<List<Tuple<int, int>>> b = new List<List<Tuple<int, int>>>();

        for (int i = 0; i <= S.Max() - '0'; i++)
            b.Add(new List<Tuple<int, int>>());

        for (int i = 0; i < N; i++)
        {
            string line = Console.ReadLine();
            a.Add(line);
        }

        for (int i = 0; i < a.Count; i++)
            for (int g = 0; g < a[i].Length; g++)
                if (S.Contains(a[i][g]))
                    b[a[i][g] - '0'].Add(Tuple.Create(g, i));

        for (int i = 0; i < b.Count; i++)
        {
            if (b[i].Count == 0)
                continue;
            Console.Write("{0}: ", i);
            Console.WriteLine(String.Join("|", b[i]).Replace("(", "").Replace(")", "").Replace(",", "").Replace("|", ", "));
        }
    }
}




C++:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <vector>
#include <string>

int main()
{
	std::string a,b;
	std::vector<std::string> c;
	std::vector<std::vector<std::pair<int, int>>> d;
	std::cin >> a;
	std::cin.ignore();
	d.resize(a[a.size()-1]-'0');

	while (std::getline(std::cin, b)&&!b.empty())c.push_back(b); //Can Loop On Input

	for (int i = 0; i < c.size(); i++)
		for (int j = 0; j < c[i].size(); j++)
			if (a.find(c[i][j]) != std::string::npos)
				d[c[i][j] - '0'].push_back(std::make_pair(j, i));

	for(int i = 0; i < d.size(); i++)
	{
		if (d[i].size() == 0)continue;
		std::cout << i << ": ";
		for (int q = 0; q < d[i].size(); q++)
		{
			std::cout << d[i][q].first << ' ' << d[i][q].second;
			if(q+1!=d[i].size())
				std::cout << ", ";
		}
		std::cout << '\n';
	}
			
}



C++ equivalent looks cleaner.. or is it just me?
Last edited on
1
2
Console.WriteLine(String.Join("|", b[i]).Replace("(", "").Replace(")", "").
Replace(",", "").Replace("|", ", "));

1
2
3
string q = String.Join("|", b[i]);
q=q.Replace("(", ""); q=q.Replace(")", "");
q = q.Replace(",", ""); q = q.Replace("|", ", ");

This looks like a pretty inefficient way. String are immutable so you create a lot of new strings, which need to be garbage collected
the reverse is true too. To connect to a remote interface and send it commands, or talk to a database, etc, is a fraction of the code in C#. C# is a nice language; its biggest issue is that it inherited OSO from java. (Objects for the sake of objects -- its unfriendly to procedural style or other ideas). But it would be my #2 go-to language, if C++ suddenly vanished. Oh, and one other issue, its a little weird on unix (or was, not sure of current state).

Last edited on
all copies are shallow by default
You can do semi-deep copies (i.e. you create a new list that points to the same objects) easily, but it's hideous:
 
var list2 = list.Where(true).ToList();


While coding, I used a List of a List of tuple<int, int> which when outputted produces "( )" around the numbers by default.. Since it was a coding contest, my output had to match. So to get rid of the that extra garbage I had to do this
Why not directly stringify the data the way you wanted?
 
list.Select(x => $"{x.Item1}|{x.Item2}")
Or, more efficiently, if more wordily, with a StringBuilder.

Wait until you try to deal with resources. One time I was writing a 5-10-kLOC program that dealt with many streams that I needed to get disposed at precisely the right times, or the program was incorrect. I became so frustrated that I could never be sure whether I was writing the Dispose() implementations correctly or whether I had forgotten a using somewhere that I just scrapped it and rewrote it in C++. RAII is such a powerful tool, I'm still shocked so many people are willing to sacrifice it in favor of tracing garbage collection.

C# is very programmer-efficient as long as your logic can tolerate working approximately-right. You don't care too much when or how things happen, but that they happen. If you need tight control over the behavior then it's unusable. I've seen the developers themselves say as much when asked about the details of how something worked (actually it was in an F# talk, but it's basically the same): "if you need fine control over this, you should use a different language."

C# is a nice language; its biggest issue is that it inherited OSO from java. (Objects for the sake of objects -- its unfriendly to procedural style or other ideas).
Nah, you can write procedurally just fine. Just put your non-object functions in a static Utility class.
I think its biggest issue is that it has basically no code generation tools. For example, if you need a bunch of methods that are all the same but with a little change, you're basically screwed. You can't even include another file in the middle of your class, so that you can generate it from outside. IMO it desperate needs some kind of hygienic macro system. Or something, because right now it has nothing. Not counting serialization and such, the coolest trick I've seen it do is Dispose() all IDisposable objects via reflection, which at best puts it in the same position as default C++ behavior, only the programmer has to implement it manually.

Oh, and one other issue, its a little weird on unix (or was, not sure of current state).
Mono works basically perfectly. The only issue I've had was when I needed to use SQLite, which is a bit of a pain because it needs a native module to work. Sometimes I've had desktop programs not start, but they were just curiosity tests so I didn't investigate what was going on or how difficult it would have been to fix them. The times I did care, they ran fine.

On the subject of distribution, one thing that does freak me out a bit is that nuget only downloads binary packages, not sources. A few days ago I removed all my packages from a particular project and added the sources to the solution because I wanted to make sure the project would still work ten years from now.
> S.Max() - '0'
¿what? from c++ a[a.size()-1]-'0' (which could be written as a.back()-'0')
¿so string::Max gives you the last element? (can't find the documentation)


> List<string> a = new List<string>();
«java is so stupid that you need to tell it twice what do you want» (some book about Smalltalk, I think)
I don't understand, ¿is so potentially destructive to create an object that you need confirmation? you know what class you want
if you say that you want to do Base foo = Derived; just in case you later change the Derived type (and no touching the rest of the code) then Derived foo; will work the same, and if it doesn't then you couldn't simply change the class in the first place

by the way, it seems that c# allows var foo = new Asdf;
Why not directly stringify the data the way you wanted?
list.Select(x => $"{x.Item1}|{x.Item2}")
Or, more efficiently, if more wordily, with a StringBuilder.

I gave it a single quick try right now, but it only outputted the type of object rather than the value. I'll try again later - though I'm not sure if it'll work. I'm not use to C# rules yet, and sometimes things that seem like the perfect solution just don't work :(.

¿what? from c++ a[a.size()-1]-'0' (which could be written as a.back()-'0')
¿so string::Max gives you the last element? (can't find the documentation)

.Max() gives back the max element in the array (in this case the highest ascii valued char). I did it the safe way in C#, and then I got lazy with C++. I knew that the input always had the max element at the end, so I simply took the last element.

RAII is such a powerful tool, I'm still shocked so many people are willing to sacrifice it in favor of tracing garbage collection.

Same ;-;

I don't understand, ¿is so potentially destructive to create an object that you need confirmation? you know what class you want

It's not bad, it just leaves you scratching your head. Why does the programmer have to do this? I don't understand why C#/Java make objects pointers by default. Why can't it allocate memory be default UNLESS the programmer sets the variable to null or uses new?

by the way, it seems that c# allows var foo = new Asdf;

That's true, but my C++ habit of writing the variable type makes me end up writing it twice (or letting VS predict it for me).
Last edited on
I don't understand why C#/Java make objects pointers by default.
You need dynamic allocation in a tracing collection system, otherwise you can't move the objects around to compact memory. A system where some objects are in one place and others are in another is possible, but more complex. To my knowledge, no one has ever done a GC'd language with stack allocation. Java has been promising that feature for years.

Why can't it allocate memory be default UNLESS the programmer sets the variable to null or uses new?
Because Java was designed in the '90s, when C++ was booming and everyone was writing servers in C++. Sun was trying to win over C++ programmers who were tired of dealing with memory errors. They wanted to give those programmers a familiar syntax that clearly communicated what was happening. Since in Java all objects are dynamically allocated and the syntax for that in C++ is Type *object = new Type();, they brought that syntax over, obviating the now redundant asterisk (since all names are references anyway [except for primitives]). They could have chosen any conceivable syntax, but anything too weird would have been a harder sell.
C# was obviously trying to ape Java, so MS copied that syntax. C# (and fairly later Java) eventually added rudimentary type deduction, which obviated the redundant type specification. Now the syntactic elements are basically the same as in C++'s automatic allocation syntax, just in a different order.
...non-object functions in a static Utility class.


that is my point, exactly. OOP is one of many ways to design code. Having a language that forces you to use only one approach is less flexible. I am surely in the minority but I find C++'s ability to have a stand-alone function wonderful. Poking it into a functor works but its ugly and the result of a language deficiency.
Last edited on
I was able to simplify the line more as you suggested Helios:

Console.WriteLine(String.Join(", ", b[i].Select(x => string.Format("{0} {1}", x.Item1, x.Item2))));

Now the syntactic elements are basically the same as in C++'s automatic allocation syntax, just in a different order.

True enough, just have to get use to typing var.

I am surely in the minority but I find C++'s ability to have a stand-alone function wonderful

Those are nice. I haven't gotten in OOP enough with C# to really know if the way it does it will bother me or not. Making a function is easy enough, and the ability to create the classes you need within the one that contains main means that those classes should be able to access functions there. But as soon as you make a class outside of that, you'll need a new version of the functions needed :(
But as soon as you make a class outside of that, you'll need a new version of the functions needed
What do you mean?
What do you mean?

Since you can't have nonmember functions, a class must be lower in the hierarchy of the class that contains the function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;

class S
{
    public static void o()
    {
        Console.WriteLine("OUTPUT");
    }

    class Cat
    {
        public static void Test()
        {
            o(); //Just Fine
        }
    }

    static void Main()
    {
        Cat.Test();
        Dog.Test();
    }
}

class Dog
{
    public static void Test()
    {
        o(); //Error
    }
}
Which just means the code is poorly structured. You don't put a class inside another just to use the outer class' methods. You do it because the two classes are tightly coupled and one doesn't make sense without the other. If you just want to call members of another class you just do it: S.o();
This is exactly the same sort of complaint that some Java programmers make when they use new for absolutely everything and then complain that their memory leaks. If you use a language the wrong way just so you can pretend it's a different language, your code is gonna suck.
If you use a language the wrong way just so you can pretend it's a different language, your code is gonna suck.

Hasn't been a problem, just feels like a missed feature.
I'm confused as to how it's a missed feature (that is, I see no desire for it, even if it were valid). If I have two classes, each with a static function with the same name, how could your Dog class possibly know which one to call? Why should it magically know that o() is referring to S.o()?

It would be analogous to:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>

class S
{
  public:
    static void o()
    {
        std::cout << "OUTPUT\n";
    }

    class Cat
    {
      public:
        static void Test()
        {
            o(); //Just Fine
        }
    };
};

class Dog
{
  public:
    static void Test()
    {
        o(); //Error
    }
};

int main()
{
    S::Cat::Test();
    Dog::Test();
}

 In static member function 'static void Dog::Test()':
28:11: error: 'o' was not declared in this scope

It's an error in C++ as well as C#.
Last edited on
I'm confused as to how it's a missed feature

Here's an example showing what I'm actually talking about:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>

void o()
{
    std::cout << "OUTPUT\n";
}

class S
{
  public:
    class Cat
    {
      public:
        static void Test()
        {
            o(); //Just Fine
        }
    };
};

class Dog
{
  public:
    static void Test()
    {
        o(); //Just Fine
    }
};

int main()
{
    S::Cat::Test();
    Dog::Test();
}



As far as I know, you can't have a standalone function - that's the missed feature. The code I showed was just to show how an independent function would have been more suitable in that situation.
It's not missed, because nobody needs it. Put procedural functions in static classes.
It's not missed, because nobody needs it.
\
If X shouldn't be missed based on need, then if we obliterate all high level programming languages they also shouldn't be missed - you can code it all in 1s and 0s.

Again, I haven't done enough OOP with C# to know whether or not it's an actual inconvenience, but I can think of a few scenarios where it would make more sense to use an independent function rather than one that's class specific.
then if we obliterate all high level programming languages they also shouldn't be missed - you can code it all in 1s and 0s
Yeah, good luck getting anything done in a reasonable time frame.

I can think of a few scenarios where it would make more sense to use an independent function rather than one that's class specific.
Yeah. And in those scenarios you use a static class with public static functions. I mean, is it such a problem to call Utility.O() instead of o()?
Yeah, good luck getting anything done in a reasonable time frame.

All needs are just means to a want.

Yeah. And in those scenarios you use a static class with public static functions. I mean, is it such a problem to call Utility.O() instead of o()?

If someone makes a general function that all classes want to use, it would just make more sense to have it independent of all classes.

Again, it hasn't been a problem so far, just seems like something you should be able to do.
How is a public static function dependent on any class? You can call it from any class without instantiating any class in particular.
Pages: 12