The problem
The problem is that while -buildmode=c-shared (and -buildmode=c-whatever in general) do indeed make the Go symbols callable from C, this comes with a twist when it comes to strings.
In C, there really is no such thing as a string but a convention exists that a string is a pointer to its first byte, and the length of a string is implicitly defined by a byte with code 0 (ASCII NUL) in that string.
In Go, strings are structs of two fields: a pointer to a memory block containing the string's contents and the number of bytes in that block.
As you can see, when the Go toolset compiles a Go function marked as "exported to C", it basically has two choices:
- Make the function callable "as is"—requiring the callers to pass a Go-style 
struct value describing a string. 
- Artifically "wrap" the exported function into a code block which would take a "C-style" NUL-terminated string, count the number of bytes in it, cook a Go-style 
struct value and finally call the original function. 
The second approach could arguably be simpler for non-Go programmers to deal with but there exist two arguments against using it:
- This approach incurs hidden cost for each call: scanning the bytes of the string—even if the caller knows it.
 
- It's possible—if needed—to create such a wrapper right in the Go code—as @Selvin suggested in their comment to the question.
 
So, to reiterate, when the Go toolset compiles a Go function marked as "exported to C" in a c-whatever mode, it follows the Go convention and the result of its work is:
- The compiled library expects to receive a 
struct-typed value comprised of a pointer and a size—as described above—for each argument of Go type string of each exported function. 
- The supporting C header file is be generated, containing the definition of that 
struct type—it will be called GoString,—and a declaration for all your exported functions, using that GoString type for the arguments which are string on the Go side. 
To say it in more simple words, if you were to create a file foo.go containing
package main
import "C"
import "fmt"
//export PrintIt
func PrintIt(string s) {
    fmt.Println(s)
}
and were then compile it using go build -buildmode=c-shared -o foo.so foo.go,
the Go toolset would create both foo.so and foo.h, containing—among other things—somethig like:
typedef struct { const char *p; ptrdiff_t n; } GoString;
extern void PrintIt(GoString p0);
As you can see, to call PrintIt, you're supposed to pass it an instance of GoString, not a value of type const char *.
A solution
A proper solution is to have something like
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential, Pack=0)]
struct GoString {
    byte *p;
    int  n;
}
And then
[DllImport("./main.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void Println(GoString gs);
Note that I'm not quite sure plain int is OK to be used for ptrdiff_t in each and every case—please do your own research on what should be properly be used for in in .NET interop.