Search code examples
c#.net-coref#solutionc#-to-f#

F# library that subclasses a class from a C# library


I want to convert the C# classes in my console app to F#, one at a time. With stunning help from Co-pilot, I got one class done. But I can’t build with it. So I made a tiny test solution, to investigate. I’m using .NET 7.0 SDK (v7.0.201)

The C# compiler complains:

  • Program.cs(7, 14): [CS0712] Cannot create an instance of the static class 'Class2'
  • Program.cs(7, 1): [CS0723] Cannot declare a variable of static type 'Class2'
  • Program.cs(8, 29): [CS1061] 'Class2' does not contain a definition for 'Greeting' and no accessible extension method 'Greeting' accepting a first argument of type 'Class2' could be found (are you missing a using directive or an assembly reference?)
  • Program.cs(8, 49): [CS1061] 'Class2' does not contain a definition for 'Number' and no accessible extension method 'Number' accepting a first argument of type 'Class2' could be found (are you missing a using directive or an assembly reference?)

I’m just learning how to write a class in F#, and I can’t see how to fix this.

#!/bin/bash

dotnet new sln 

dotnet new console -o Console
dotnet new classlib -o LibraryC 
dotnet new classlib -lang "F#" -o LibraryF

dotnet sln add Console/Console.csproj 
dotnet sln add LibraryC/LibraryC.csproj  
dotnet sln add LibraryF/LibraryF.fsproj 

dotnet add LibraryF reference LibraryC
dotnet add Console  reference LibraryF

cat << 'EOT' > LibraryC/Class1.cs
namespace LibraryC;

public class Class1 {

  public virtual int Number => 17;

  public virtual string Greeting() => "Hello";

}
EOT

cat << 'EOT' > LibraryF/Library.fs
namespace LibraryF

open LibraryC

module Class2 =   // ANSWER: Remove this.

  type Class2() =     
    inherit Class1()
    
    member this.Junk() = "not static"
      
    override this.Number = 42

    override this.Greeting() =
      let fromBase = base.Greeting()
      $"{fromBase} from Class2 {this.Number}"
EOT

cat << 'EOT' > Console/Program.cs
using LibraryC;
using LibraryF;

var class1 = new Class1();
Console.WriteLine($"{class1.Greeting()} {class1.Number}");

var class2 = new Class2();
Console.WriteLine($"{class2.Greeting()} {class2.Number}");
EOT

I wrote the above script by hand. This way of exporting a solution seems very useful, so I opened an issue for a dotnet command to export a .sln file as a sequence of dotnet commands.


Solution

  • As mentioned in the comments, you are declaring a module Class2 inside LibraryF and only inside this module, you have a class Class2. F# modules are compiled as static classes and so if you open LibraryF, the Class2 refers to the static class representing the module.

    You can resolve this by dropping the module declaration from the F# library (you would only need a module if you also wanted to have top-level let bindings such as functions or global values, but if you do not need those, you can put the class directly inside a namespace):

    namespace LibraryF
    open LibraryC
    
    type Class2() =     
      inherit Class1()
        
      member this.Junk() = "not static"
      override this.Number = 42
      override this.Greeting() =
        let fromBase = base.Greeting()
        $"{fromBase} from Class2 {this.Number}"