Short of "not distributing them" there is no 100% sure way to prevent unauthorized access.
You could look into hardware or software licensing devices. Sprinkle license checks throughout your code and if the device is not present simply abort everything.
Another idea and is to declare all your types in the assembly as internal
then setup your main application EXE as a friend assembly with the InternalsVisibleTo
assembly attribute. This is typically used for unit-testing internal members and I have no idea how secure it would be in practice. This would not prevent people from disassembly your assembly so you may still want to obfuscate and this doesn't work at all if you are selling the library and intend only for licenses customers to use it (because you would have to provide custom builds to every customer).