Should I cache instances of RNGCryptoServiceProvider / RandomNumberGenerator?

I read many times about the effectiveness of the Regex class and how important it is to either call it static methods or cache an instance of regex.

I would like to know if the same problems will occur if I create an instance of the RNGCryptoServiceProvider class several times, instead of caching one instance of the class and calling GetBytes on it.

By executing it every time I need a random number, I would simplify my code a bit, since I do not need to worry about the one-time instance hanging and spreading the IDisposable interface through a bunch of classes.

The only thing I found about this is that creating an instance of RNGCryptoServiceProvider should be very fast , but I still would like to get confirmation and that is best practice.

Were there also any differences in the generated random numbers if I created an instance of the class each time compared to using the same instance?

+5
source share
1 answer

Re-building using the default constructor should not have any negative consequences for performance quality or randomness.

Let's look at the source code ...

 #if !FEATURE_PAL [System.Security.SecuritySafeCritical] // auto-generated public RNGCryptoServiceProvider() : this((CspParameters) null) {} #else // !FEATURE_PAL public RNGCryptoServiceProvider() { } #endif // !FEATURE_PAL 

The FEATURE_PAL directive is related to windows and non-windows platforms. But we do not need to know the details; let's just look at the true and false cases.

First, it’s clear that if FEATURE_PAL is enabled, there is no code in the default constructor.

In another case, the constructor invokes a specific constructor with null CspParameters. This other constructor looks like this:

 [System.Security.SecuritySafeCritical] // auto-generated public RNGCryptoServiceProvider(CspParameters cspParams) { if (cspParams != null) { m_safeProvHandle = Utils.AcquireProvHandle(cspParams); m_ownsHandle = true; } else { m_safeProvHandle = Utils.StaticProvHandle; m_ownsHandle = false; } } 

cspParams will always be null, so the constructor gets the value Utils.StaticProvHandle . This receiver is as follows:

 #if !FEATURE_PAL [System.Security.SecurityCritical /*auto-generated*/] private static SafeProvHandle _safeProvHandle = null; internal static SafeProvHandle StaticProvHandle { [System.Security.SecurityCritical] // auto-generated get { if (_safeProvHandle == null) { lock (InternalSyncObject) { if (_safeProvHandle == null) { SafeProvHandle safeProvHandle = AcquireProvHandle(new CspParameters(DefaultRsaProviderType)); Thread.MemoryBarrier(); _safeProvHandle = safeProvHandle; } } } return _safeProvHandle; } } #endif // !FEATURE_PAL 

It is supported by a static variable. The getter uses some locks during the first initialization, but subsequent calls simply return a static variable.

Now look back at RNGCryptoServiceProvider.cs and look at the Dispose method:

 [System.Security.SecuritySafeCritical] // auto-generated protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing && m_ownsHandle) { m_safeProvHandle.Dispose(); } } 

m_ownsHandle is false if the default constructor was called, so it never has anything.

Thus, everything that happens during each build + is simple access to the variable.

+8
source

Source: https://habr.com/ru/post/1204682/


All Articles