Skip to content

So many small mistakes  #13466

@ericlippert

Description

@ericlippert

This page was brought to my attention by https://stackoverflow.com/questions/57097636/what-does-fidelity-of-accessor-keywords-mean/57099575#57099575

There are so many small mistakes and bad uses of jargon here. A few highlights:

> C# is simple, powerful, type-safe, and object-oriented

The specification is 800 pages long. The compiler is a million lines of code. Yes, I know page one of the spec says that C# is simple. It has not been simple for over a decade. C# is an incredibly complex language.

> The GC operates on a lazy approach to memory management, preferring app throughput to the immediate collection of memory.

First off, the grammar of that sentence is tortured. "operates on a lazy approach"? What interesting word-usements you enstructure.

Second, you're describing an implementation detail and a design goal, neither of which are relevant to the user who cares not a bit about that. SAY WHAT THE BENEFIT IS TO THE USER. Automatic garbage collection frees the developer from having to worry about tracking every byte of memory; the performance penalty of automatic GC is comparable to the performance penalty of manual memory management but with less chance of bugs and with shorter code.

> The following two lines both allocate memory:

I'm not sure what the point of this example is, but there is a trick missed. The example could point out that the memory for the variables is every bit as much memory as the memory that is referred to, and that the GC will take care of ensuring that the lists and strings will be alive for at least as long as the program is using the variables. Again, say what the benefit is to the user.

> The garbage collector is one of the services that help ensure memory safety.

Um, what? The example given is caught by the code generated by the jitter, not by the GC. If you're going to make the argument that the GC enforces memory safety, give an example where the GC enforces memory safety, not where the jitter generates code that enforces memory safety.

> When you're done using the FileStream, you need to release the file handle.

This is a slight overstatement. In a properly built system, disposing is the thing that you should do, not the thing that you must do. Disposing is efficient and polite; it says "because I know that someone else might want to access this file, I will let the runtime know that I'm done with it rather than waiting for the GC to clean up the file stream for me".

> An object is an instance of a specific type.

What is the function of the word "specific" here? Take a string. A string is an instance of string, object, IEnumerable, IEnumerable<char> and a bunch more types. Which one of those is the "specific" type?

> The only operations allowed for a given object are those of its type.

This is a definition of "type safety". Be specific. Say what you're defining. "By 'type safety' we mean that the only operations..."

> Dog dog = AnimalShelter.AdoptDog(); // Returns a Dog type.

It returns an instance of type Dog, or an object of type Dog not "a Dog type".

> Pet pet = (Pet)dog; // Dog derives from Pet.

Why the cast then?

> Car car = (Car)dog; // Will throw - no relationship between Car and Dog.

NO NO NO. This code does not even COMPILE so how could it THROW?

> object temp = (object)dog; // Legal - a Dog is an object.

So again, why the cast?

> Type safety is also used to help enforce encapsulation by guaranteeing the fidelity of the accessor keywords. Accessor keywords are artifacts which control access to members of a given type by other code. These are usually used for various kinds of data within a type that are used to manage its behavior.

This impenetrable paragraph was the one that so confused a user that they posted it on SO. "guaranteeing the fidelity of the accessor keywords"? What the heck is this? The accessor keywords are "get" and "set"; what do they have to do with this example? Nothing whatsoever. Somehow the author has confused property accessors with accessibility levels. Delete this paragraph and start over.

Also, stop saying "type safety is used..." Type safety is an emergent property of the language. How do we get there? Static and runtime type checking. That's the thing that does the work -- the static and runtime type checkers.

> Remember that every type defined in any .NET language derives from the base Object type.

It is unclear what is meant here by "defined in any .NET language". Is Object "defined in any .NET language?" There's a C# file that has the definition of Object in it, so yes it is, but Object does not derive from Object.

Is "int*" "defined in any .NET language?" It is not convertible to Object; are you trying here to make the subtle distinction between a type that is "defined in a language" and one that is, what, a natural part of the .NET type system? Because this is a subtle distinction that will be lost on the reader; the reader will likely believe that you mean that every type is derived from object, which is false.

But plainly interfaces are "defined in any .NET language". Are interfaces derived from object? Give that some thought! Every reference to an interface is convertible to a reference to object in C#, but convertibility and derivation are different relations. C# allows you to use the members of object on an instance of an interface, but again, does that imply a derivation relationship? It is not clear whether interfaces have a derivation relationship, or just a convertibility relationship!

> The .NET runtime only allows object casts and calls that align with the object hierarchy.

"align with the object hierarchy" means what exactly?

The author seems to be using "cast" to mean "conversion". Try to say "conversion" when you mean "conversion"; casting is an operator that facilitates conversions.

> A delegate is represented by a method signature. Any method with that signature can be assigned to the delegate and is executed when the delegate is invoked.

I can't make heads nor tails of this. Start by distinguishing between a delegate type, and an instance of a delegate type, since both are confusingly called "delegates". Second, you're using "method signature" without clearly defining it, and in a way that is different from how the C# language defines method signature. (For example, in C# a method signature does include generic arity but does not include return type, but delegates are the opposite.) Third, the convertibility of a method group to a delegate type is more complicated than mere signature matching; C# has supported variance in delegate compatibility longer than it has supported generic variance.

Start over. Again, focus on the benefit to the user: A delegate is an object that can be invoked like a method, and thereby allows you to pass around methods like any other data. Delegates are instances of delegate types; each delegate type describes the formal parameter types and return types of the method. Delegates can be created by a variety of mechanisms, including converting existing methods to delegates and defining new anonymous methods with lambda expressions.


Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

  • ID: 350a9ca4-1407-4f80-0e3c-05609687c014
  • Version Independent ID: 6ae6a540-0822-5e2d-cf75-97f064fa0961
  • Content: Tour of .NET
  • Content Source: docs/standard/tour.md
  • Product: dotnet
  • Technology: dotnet-standard
  • GitHub Login: @cartermp
  • Microsoft Alias: wiwagn

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions