Precise definition of oracle classes $A^B$
The simplest case is when $C$ has complete problems with respect to $\mathsf{F}B$ reductions (the function class corresponding to $B$). In that case, you can define $B^C$ as $B^L$ for some $C$-complete problem $L$, and everybody will be (mostly) happy, at least if $B$ is not a very weak class. This allows us to define $\mathsf{P}^{\mathsf{NP}}$, for example.

When $C$ doesn't have complete problems, sometimes the following definition is used: $B^C = \bigcup_{L \in C} B^L$, or perhaps $B^C = \bigcup_m \bigcup_{L_1,\ldots,L_m \in C} B^{L_1,\ldots,L_m}$. However, this definition doesn't necessarily have the right properties.

Another option is if you can enumerate all languages in $C$ (for example, $\mathsf{BPP}$ can be enumerated using probabilistic Turing machines with an explicit time bound), say they are $(L_n)_{n \in \mathbb{N}}$. In that case you can define $B^C = B^{L_1,L_2,\ldots}$, where the oracle for $L_1,L_2,\ldots$ accepts an index $i$ and a word $x$, and outputs whether $x \in L_i$. One drawback is that this definition depends on the enumeration; it's best to use a "natural" enumeration. Otherwise, you can "cheat" by putting information into the enumeration. A way out it to define $B^C$ as the intersection of $B^{L_1,L_2,\ldots}$ for all possible enumerations.

Things become even more complicated when $B$ is weak. For some of the issues involved, see Aehlig, Cook, and Nguyen, Relativizing Small Complexity Classes and their Theories. These issues are orthogonal to what we have described above: they cause problems even when trying to define $B^L$ for a language $L$.