string versus String ? fejl eller feature i C#

Ind imellem må man virkelig undre sig. Det skete for eksempel for mig første gang og 2. og 3. og 4. og ?jeg er faktisk ikke færdig med at undre mig, over forskellen på string og String i C#. Jeg kan se at jeg ikke er den eneste, som er stoppet op ved mødet med disse to typer, en hurtig søgning på emnet giver 268.000 hits. Jeg fandt følgende forklaring på Microsofts hjemmeside:
 
In C#, the string keyword is an alias for String. Therefore, String and string are equivalent, and you can use whichever naming convention you prefer.
 
Hmm, så blev vi så kloge. I disse 'spild endelig ikke et minut på noget unødvendigt'-tider, skulle man synes at Microsoft skylder en lidt bedre forklaring til alle os, som har brugt adskillige minutter på sagen.
 
Findes der mon nogle mere oplyste personer derude, som kan kaste et andet lys over sagen og måske på den måde sørge for, at ikke alle minutterne er spildte? Det kan være du ligger inde med en typeteoretisk forklaring. Det kan være du har en æstetisk forklaring (de to typer er henholdsvis mørkeblå og lyseblå i Visual Studio). Eller det kan være du kender til saftige historier om slagsmål blandt Microsofts C# designere.

Kommentarer (21)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
Thomas Gravgaard

Der er vist ikke noget dybere i det end det du skriver - det er et alias. C# har sine egne reserverede ord og en dialekt der mapper til .NET typer. Ligesom int => Int32 oma. Andre .NET sprog har det på samme måde så man kan brige sproget som man kender det. Det virker lidt paradoksalt med C# der ikke havde et liv før .NET og derfor kunne have lagt sin dialekt op ad .NET frameworket. Men de har sikkert holdt på at de reserverede or skulle være lower cased af konsistensgrunde og derfor gik String ikke.

Mener nok at huske at der i den oprindelige C# style guide stod at man bør bruge "string" og ikke "String" - da man så var sikker på at bruge C#'s type og ikke .NET's type. De jo kunne finde på at ændre mappingen... Tyg lidt på den. Med string er det nok knap så relevant som det kunne blive i frentiden med en mapping fra int => Int64 i en fremtid hvor alle kører 64 bit maskiner.

  • 0
  • 0
Martin Nissen

Det er endnu en Microsoft gåde, som ingen nogensinde får et fornuftigt svar på.
Og svaret "Therefore, String and string are equivalent..." kommer ikke engang tæt på.
En grøn sodavand der smager som en rød, er stadig en grøn sodavand!

Men gad vide hvor mange MB lettere Windows ville blive hvis sådanne tilfælde blev omskrevet.

  • 0
  • 0
Brian Rasmussen

C# er jo blot et af flere sprog, der bygger på Microsofts Common Language Specification (CLS). Det er CLSen, der specificerer de enkelte typer som f.eks. String. De enkelte sprog kan repræsentere typerne, som de vil. I C# har man både et alias i form af "string" samt selve typen med samme navn som i CLSens repræsentation. Det er intet til hindring for at et helt tredje sprog kalder typen for "text" eller lignende og mapper til den underliggende String-type.

  • 0
  • 0
Daniel Møller

Thomas kommer reelt ind omkring problematikken men jeg kan lige prøve at sætte lidt andre ord på :-)

Årsagen kommer sig af at .NET er en multi-sprogs platform. For at sprogene bedrer kan spille sammen er der defineret et Common Type System (CTS).

Da disse typer er defineret sprog-neutralt følger de ikke gængs C-navngivning og det er formentlig grunden til at man har valgt at indføre en type-mapping i C# - således at typerne kan defineres med navne C++ udviklere er vant til at arbejde med.

Men der er blot tale om et sprogligt alias, I den resulterende IL kode er der ingen forskel.

  • 0
  • 0
Mogens Heller Grabe
  • og det er at C#-sproget ikke direkte afhænger af det underliggende .NET-framework. Som Thomas Gravgaard nævner, så kunne man jo forestille sig at mapningen en dag blev ændret til en anden klasse end lige System.String i .NET-frameworket.

Det samme gør sig gældende med LINQ - altså sprogudvidelsen med det indbyggede DSL til querying. At C#-compileren så i øjeblikket mapper fra LINQ-udtryk til en stor samling extension methods er så bare en måde at implementere en kompilering til .NET.

  • 0
  • 0
Daniel Møller

Nu kan jeg se det foregående indlæg nævner at de er repræsenteret i CLS'en, hvilket faktisk er mere korrekt - da CLS repræsenterer et subset af typerne fra CTS som alle sprogene skal understøtte og det var reelt dette subset jeg tænkte på i mit indlæg.

Beklager forvirringen :)

  • 0
  • 0
Daniel Møller

Man kan godt forestille sig det i teorien, men i praksis vil det næppe ske og derfor er der for alle praktiske formål ikke forskel på at skrive String eller string :)

LINQ er iøvrigt framework-uafhængigt, det står dig frit for at definere din egen LINQ-implemenation, som er uafhængig af extension methods og lign. defineret i .NET frameworket. Der er altså ikke tale om en direkte mapping - der er blot nogle konkrete LINQ implementationer til hhv. object-, XML- og SQL-queries, men der er intet i vejen for at lave din egen XML-2 implementering f.eks.

  • 0
  • 0
Jørn Schou-Rode

derfor er der for alle praktiske formål ikke forskel på at skrive String eller string :)

Jo, det kan faktisk godt være forskelle. Nedenstående C#-program kan kompiles, men det kun såfremt string skrives med småt:

[code=csharp]class Foo
{
string GetText() { return "Hello World"; }
}[/code]

Skriver jeg i stedet "String", er det nødvendigt at importere navnerummet System via en "using" erklæring.

Et andet problem: du arbejder på et projekt hvor du har brug for at lave nogle DNA-sammenligninger, og indkluderer derfor biblioteket BiologyLib.DNA, som bl.a. indeholder en String klasse til repræsentation af DNS-strenge:

[code=csharp]using System;
using BiologyLib.DNA;

class Foo
{
String GetText() { return "Hello World"; }
}[/code]

Oversætteren giver en fejl i stil med: 'String' is an ambiguous reference between 'string' and 'BiologyLib.DNA.String'

Ved i stedet at bruge nøgleordet "string", fortæller vi såvel oversætteren som andre læsere af vores kode, at vi ønsker at arbejde med en helt almindelig .NET-tekststreng. Refererer vi direkte navnet på den konkrete implementering, bliver vi nødt til at tilføje anvisninger på hvor klassen skal findes.

  • 0
  • 0
Nicolai Lundgaard

De problemer du skildrer har intet at gøre med string vs. String.

  1. Du skal selvfølgelig inkludere det navnerum der indeholder definitionen af String, som du skal med alle andre typer. Alternativt kan du skrive typens fulde navn fx. System.String.

  2. Der er ikke noget ualmideligt ved tvetydige navne. I sådanne tilfælde er man nødt til at specificere det fulde navn på typen.

  • 0
  • 0
Jørn Schou-Rode
  1. Du skal selvfølgelig inkludere det navnerum der indeholder definitionen af String, som du skal med alle andre typer. Alternativt kan du skrive typens fulde navn fx. System.String.

Ja, eller jeg kan bruge nøgleordet "string" og så behøves jeg ikke bekymre mig om at inkludere det relevante navnerum.

  1. Der er ikke noget ualmideligt ved tvetydige navne. I sådanne tilfælde er man nødt til at specificere det fulde navn på typen.

Ja, eller jeg kan bruge nøgleordet "string" og så behøves jeg ikke bekymre mig om risikoen for at ramme et tvetydigt navn.

De problemer du skildrer har intet at gøre med string vs. String.

Her er vi så tydeligvis uenige. Ingen af de to "problemer" ville være opstået her hvis det ikke var fordi jeg skrev "String" med stort S.

  • 0
  • 0
Jesper Lund Stocholm Blogger

Jørn,

Her er vi så tydeligvis uenige. Ingen af de to "problemer" ville være opstået her hvis det ikke var fordi jeg skrev "String" med stort S.

Men det er jo fordi du ikke svarer på det oprindelige spørgsmål, nemlig om der er forskel på objekterne "string" og "String". Svaret her er - som Nicolaj skriver - at det er der ikke, for "string" er et alias for "String", ganske som "int" er et alias for "Int32".

At du kan komme ud for tvetydigheder ved navngivning har intet med ligheden imellem "string" og "String" at gøre, for hvis du får denne tvetydighed, skyldes det jo netop, at dit "String"-objekt ikke er den i CTS indbyggede "String"-type men en helt anden.

... i øvrigt reflekteres "using"-statements ikke i den genererede IL, hvis de ikke konkret anvendes i din klasse. Deres funktion er dermed primært at få din kompiler til at gøre det rigtigt - og sikkert også intellisense og tilsvarende ting.

  • 0
  • 0
Nicolai Lundgaard

Her er vi så tydeligvis uenige. Ingen af de to "problemer" ville være opstået her hvis det ikke var fordi jeg skrev "String" med stort S.

Nej, der er vi enige. Og der ville aldrig opstå tvetydigheder, hvis jeg sørgede for at alle typer var navngivet unikt, og der ville aldrig opstå kompiler fejl, hvis jeg altid skrev fejlfri kode. Men hvad er din pointe i forhold til det oprindelige indlæg?

  • 0
  • 0
Thomas Gravgaard

Men Jørn har en pointe - som du i øvrigt understreger. Det der genereres i IL er det samme uanset om du skriver det ene eller det andet - for det er samme klasse vi snakker om. Men for C# programmøren kan han være 100% sikker på ikke at henvise til den konkrete implementation ved hjælp af using eller System.String hvis han bruger det C# reserverede ord string. Han ved at han får en tekststreng og ikke alt muligt andet der tilfældigvis er i namespacet.

Det er faktisk et argument for at bruge string og ikke String. :)

  • 0
  • 0
Jørn Schou-Rode

Jeg beklager hvis mine to indlæg tidligere i denne tråd manglede den røde tråd ift. den oprindelige artikel. Sammenhængen imellem de to mulige problemstillinger jeg nævner og diskussionen om "String vs string" er - som Thomas Gravgaard påpeger - at jeg mente at de to problemstillinger kunne bruges som argumentation for, at bruge nøgleordet frem for klassenavnet.

At "string" og "String" (såfremt vi ikke møder situationer som dem jeg beskriver) kompileres til det samme er jeg helt med på. Dette er nævnt flere gange tidligere i tråden, hvorfor jeg ikke fandt det nødvendigt at gentage denne detalje, men i stedet valgte at beskrive nogle situationer hvor brugeren (= udvikleren) faktisk oplever en forskel.

Nu hvor vi alle er enige om at string og String er det samme, vil jeg mægtig gerne høre om Jesper, Nicolai eller andre er uenige i at nøgleordet som hovedregel er det "sikreste" valg? Jeg er ikke ude på at være troll her, men er tværtimod oprigtigt interesseret i om der findes nogen saglig grund til at referere til klassenavnet når vi har et nøgleord til vores rådighed. Og dette spørgsmål må da om noget holde sig til tråden i den oprindelige artikel :)

  • 0
  • 0
Mogens Heller Grabe

Der er efter min mening også en anden pointe: nemlig at System.String er designet til at fungere som en value type (instanser er immutable, beregninger resulterer altid i nye instanser) - men det føles altså bare så reference type-agtigt at skrive String med stort.

På den måde er der lidt større sammenhæng mellem notationen og virkemåden. En lidt bøvet formulering måske, men jeg mener helt sikkert det er en gyldig pointe.

  • 0
  • 0
Nicolai Lundgaard

Ja, i dette konkrete tilfælde kan du undgå tvetydigheder ved at bruge nøgleordet, da du ikke kan definere en ny klasse der hedder "string". Omvendt undgår du også tvetydigheder hvis du bare undlader at definere en klasse der hedder "String"...

  • 0
  • 0
Daniel Møller

Jørn: Vi er vist ude i flueknepperi nu, men ja du har vel principielt ret i at man kan løbe ind i lidt flere problemer med String end string - at de problemer du nævner så vist er mere teoretiske end praktiske er så hvad det er :)

1) Hvorfor skulle man ønske at anvende en klasse uden using System; ?

2) Hvis nogen havde lavet et library hvor de rent faktisk har oprettet en klasse de kaldte "String" - så tror jeg at jeg hurtigst muligt ville se mig om efter et andet library - for hvordan ser resten af koden så ikke ud :-)

Jeffrey Richter anbefaler i bogen "Inside the .NET CLR" iøvrigt at anvende de indbyggede typer i al sin kode, grunden hertil er følgende:

1) .NET er en multi-sprogs platform, hvis man rent faktisk mixer sprogene (anvender forskellige libraries skrevet i forskellige sprog) eller har brug for at VB.NET udviklere skal kunne læse og forstå C# kode - så giver det mening at anvende de samme typenavne i alle sprogene - altså Int32 istedet int, String istedetfor string, for at mindske forvirringen.

2) Flere .NET klasser har metoder der inkluderer typenavnet i metodenavnet, f.eks. IDataReader.GetInt32, .GetInt64, .GetString osv. ved at anvende .NET typerne skal man ikke tænke over at en long f.eks. mapper til Int64.

Det var dog mest for at lege fandens advokat. Jeg er personligt tilhænger af at anvende C# aliases da jeg synes de er rarere at erklære og godt kan li at de som standard har en syntax highlight farve der er forskellig fra struct/class erklæringer :-)

  • 0
  • 0
Carsten Sonne

C# Language Specification Version 3.0 beskriver den præcise mapning i afsnit 4.1.4 (side 76).

C# Language Specification Version 3.0:
http://download.microsoft.com/download/3/8/8/388e7205-bc10-4226-b2a8-753...

Definitionen i C# er følgende:
using string = System.String;

På samme måde kan man i øvrigt lave sine egne alias hvis man skulle have lyst til det.

Af overstående kan ses at string keyworded aldrig er tvetydigt, men mapper direkte til klassen System.String.

Mere uddybende info kan finde i følgende afsnit:
2.4.4.5 String literals
4.2.3 The string type
7.9.7 String equality operators

Mvh
Carsten Sonne Larsen

  • 0
  • 0
Brian Rasmussen

Der er efter min mening også en anden pointe: nemlig at System.String er designet til at fungere som en value type (instanser er immutable, beregninger resulterer altid i nye instanser) - men det føles altså bare så reference type-agtigt at skrive String med stort

Jeg er ikke helt sikker på, hvad du mener med, at String er designet til at fungere som en value type. String er en reference type, og der er desværre ingen regel i C# om at value types skal være immutable (det er en rigtig god ide at lave dem immutable, men det sker altså ikke automatisk).

  • 0
  • 0
Carsten Sonne

Jeffrey Richter anbefaler i bogen "Inside the .NET CLR" iøvrigt at anvende de indbyggede typer i al sin kode ….

Jeg har selv læst Jeffrey Richter og er kun delvis enig i hans betragtninger. Han argumenter for at C# aldrig skulle have indført deres egne alias for datatyper, da det forvirre mere end det gavner.

C#’s aliases for datatyper er en direkte arv fra C++. Grunden til at Anders Hejlsberg og co. valgte ikke at smide den arv væk, er den simple, at C++ programmører nemmere kan genkende og forstå syntaksen i C#. Microsoft usagte mål er at få så mange C++ (og C) programmører til at skifte til C# som muligt. I vurderingen af om det gavner C# mere end det forvirre, kan man være enten enig eller uenig i. Jeg vil personligt mene at Anders Hejlsberg i denne sammenhæng traf den rigtige beslutning.

Som Daniel Møller ganske rigtig pointere er der flere .Net klasser der har navne som referer til .Net frameworkets eget typesystem. Dette er der ligeledes en ganske god grund til:
.Net frameworket er designet til at være sproguafhængigt (ikke sprogspecifikt). Derfor inkluderer det også sit eget typesystem. Andre sprog som bruger .Net frameworket (VB.Net eller Delphi.Net f.eks.) har sine egne keywords for typer. F.eks. er keywordet for et 32 bit heltal i Delphi ’Integer’. Derfor vil det være forvirrende for en programmør, der bruget et andet sprog en C#, at metodenavne er navngivet ud fra datatyper i C#. Så er det meget bedre at benytte frameworkets eget typenavne og i øvrigt også i trit med den øvrige filosofi.

I FxCop håndhæver navngivningsregel CA1718 overstående: Avoid language specific type names in parameters:
http://msdn.microsoft.com/en-us/library/ms182233(VS.80).aspx

  • og det er at C#-sproget ikke direkte afhænger af det underliggende .NET-framework. Som Thomas Gravgaard nævner, så kunne man jo forestille sig at mapningen en dag blev ændret til en anden klasse end lige System.String i .NET-frameworket.

Overstående er fuldstændigt utænkeligt. Hvis Anders Hejlsberg og co. skulle træffe den beslutning, ville det jo direkte medføre at C# ikke længere var kompatibelt med .Net frameworkets klasser. Alle klasser modtager og returnere strenge i form af System.String. Det er direkte i modstrid med filosofien i C# og selve C#’s eksistensgrundlag.

Typesystemet i C# (og .Net) kan virke forvirrende, men der er ganske tungevejende grunde til de valg der er blevet taget. Som med alle andre valg, er der fordele og ulemper. Den største ulempe er helt klart at det kan virke forvirrende (Boxing/unboxing er en af de andre store ulemper).

Anders Hejlsberg fremhæver selv typesystem i .Net som en de vigtigste (og bedste) beslutninger der blev taget dengang det blev udarbejdet. Hans argumentation er, at for mange programmører virker det underligt at der er 8 – 16 datatyper, som har en helt særlig status og implementering i forhold til alle øvrige typer. Derimod er det meget mere naturligt hvis alle typer er på lige fod med hinanden.

Mvh
Carsten Sonne Larsen

  • 0
  • 0
Log ind eller Opret konto for at kommentere