I often see copy constructors suggested as an alternative to a cloning method, but except with sealed classes the behaviors are very different. If I have a type Car, which simply supports properties VIN, BodyColor and BodyStyle, and a derivative type FancyCar, which also supports InteriorFabric and SoundSystem, then code which accepts an object of type Car and use the Car copy constructor to duplicate it will end up with a Car. If a FancyCar is passed to such code, the resulting "duplicate" will be a new Car, which has a VIN, BodyColor, and BodyStyle that match the original car, but which will not have any InteriorFabric or SoundSystem. By contrast, the code were to accept a Car and use a cloning method on it, passing a FancyCar to the code would cause a FancyCar to be produced.
Unless one wants to use Reflection, any cloning method must at its base involve a call to base.MemberwiseClone. Since MemberwiseClone is not a virtual method, I would suggest defining a protected virtual cloning method; you may also want to prevent any child classes from calling MemberwiseClone by defining a dummy nested class of protected scope with the same name (so if a descendent class tries to call base.MemberwiseClone, it wouldn't be interpreted as a nonsensical reference to the dummy class).