Sorry for being late to the party. :-)
expl3/xparse/ltcmd bring along nice tools for case-forking. Since the 2020-10-01 release expl3 and xparse (the latter re-branded as ltcmd and deprived of things that are not considered good practice any more) are included in the LaTeX 2e-format. So nowadays you could use expl3/ltcmd "out of the box" without loading additional packages. But in 2012, when the question was asked, xparse/expl3 needed to be loaded as additional packages while the question requests things to be done without loading additional packages. Thus I assume that the focus of the question is on "old school code".
You can easily avoid any \if..\else..\fi
-thingie without loading whatsoever additional packages if you do things in terms of delimited arguments:
\makeatletter
\errorcontextlines=10000
%%=============================================================================
%% PARAPHERNALIA:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@stopromannumeral, \UD@CheckWhetherNull,
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
\expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%=============================================================================
\@ifdefinable\UD@GobbleToExclam{\long\def\UD@GobbleToExclam#1!{}}%
\@ifdefinable\UD@abcFork{%
\long\def\UD@abcFork#1!a!b!c!#2#3!!!!{\UD@stopromannumeral#2}%
}%
\newcommand\dothis[1]{%
\romannumeral\expandafter\UD@CheckWhetherNull
\expandafter{\UD@GobbleToExclam#1!}{%
\UD@abcFork
!#1!b!c!{so you typed a}% <-this is \UD@abcFork's 2nd arg if #1 = a
!a!#1!c!{now this is b}% <-this is \UD@abcFork's 2nd arg if #1 = b
!a!b!#1!{you want me to do c?}% <-this is \UD@abcFork's 2nd arg if #1 = c
!a!b!c!{[nada]}% <-this is \UD@abcFork's 2nd arg if #1 neither a nor b nor c
!!!!% <-this is the delimiter of \UD@abcFork's 3rd argument
}{\UD@stopromannumeral[nada]}% <-This is done if #1 contains ! and thus could erroneously match the delimiter !a!b!c!
}%
\makeatother
\documentclass{article}
\begin{document}
\dothis{a}
\dothis{b}
\dothis{c}
\dothis{e}
\dothis{!e!}
\end{document}

If you wish to combine cases, e.g. "You typed a or d", you can combine the above forking-technique with a routine for extracting the K-th argument from a list of undelimited arguments so that the number K is provided by the forking-mechanism:
\makeatletter
\errorcontextlines=10000
%%=============================================================================
%% PARAPHERNALIA:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond,
%% \UD@stopromannumeral, \UD@CheckWhetherNull,
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
\expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%=============================================================================
%%
%% Extract K-th inner undelimited argument:
%%
%% \ExtractKthArg{<integer K>}%
%% {<tokens in case list of undelimited args doesn't have a k-th argumnent>}%
%% {<list of undelimited args>} %
%%
%% In case there is no K-th argument in <list of indelimited args> :
%% Does deliver <tokens in case list of undelimited args doesn't have a k-th argumnent.
%% In case there is a K-th argument in <list of indelimited args> :
%% Does deliver that K-th argument with one level of braces removed.
%%
%% Examples:
%%
%% \ExtractKthArg{0}{not available}{ABCDE} yields: not available
%%
%% \ExtractKthArg{3}{not available}{ABCDE} yields: C
%%
%% \ExtractKthArg{3}{not available}{AB{CD}E} yields: CD
%%
%% \ExtractKthArg{4}{not available}{{001}{002}{003}{004}{005}} yields: 004
%%
%% \ExtractKthArg{6}{not available}{{001}{002}{003}} yields: not available
%%
%% Due to \romannumeral-expansion the result can be obtained by triggering
%% two expansion-steps.
%%=============================================================================
\newcommand\ExtractKthArg[2]{%
\romannumeral%
% #1: <integer number K>
% #2: <action if there is no K-th argument>
\expandafter\UD@ExtractKthArgCheck
\expandafter{\romannumeral\number\number#1 000}{#2}%
}%
\newcommand\UD@ExtractKthArgCheck[3]{%
\UD@CheckWhetherNull{#1}{\UD@stopromannumeral#2}{% empty
\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}%
}%
}%
\begingroup
\def\UD@ExtractFirstArgLoop#1{%
\endgroup
\@ifdefinable\UD@RemoveTillFrozenrelax{%
\long\def\UD@RemoveTillFrozenrelax##1##2#1{{##1}}%
}%
\newcommand\UD@ExtractKthArgLoop[3]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo##3{}.}{\UD@stopromannumeral##2}{%
\UD@CheckWhetherNull{##1}{%
\UD@ExtractFirstArgLoop{##3#1}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}##3}%
{\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}##1}{##2}}%
}%
}%
}%
}%
\expandafter\expandafter\expandafter\UD@ExtractFirstArgLoop
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\ifnum0=0\fi}%
%% Usage of frozen-\relax as delimiter is for speeding things up by reducing the
%% amount of iterations needed. I chose frozen-\relax because David Carlisle
%% pointed out in <https://tex.stackexchange.com/a/578877>
%% that frozen-\relax cannot be (re)defined in terms of \outer and cannot be
%% affected by \uppercase/\lowercase.
%%
%% \UD@ExtractFirstArg's argument may contain frozen-\relax:
%% The only effect is that internally more iterations are needed for
%% obtaining the result.
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{\expandafter\UD@stopromannumeral\UD@firstoftwo#1{}}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillFrozenrelax#1}}%
}%
%% End of code for \ExtractKthArg.
%%=============================================================================
\@ifdefinable\UD@GobbleToExclam{\long\def\UD@GobbleToExclam#1!{}}%
\@ifdefinable\UD@abcdeFork{%
\long\def\UD@abcdeFork#1!a!b!c!d!e!#2#3!!!!{#2}%
}%
\newcommand\dothis[1]{%
\romannumeral\expandafter\UD@secondoftwo
\ExtractKthArg{%
%==\ExtractKthArg's 1st argument=<integer K>===============================
\expandafter\UD@CheckWhetherNull\expandafter{\UD@GobbleToExclam#1!}{%
\UD@abcdeFork
!#1!b!c!d!e!{1}% <- #1 = a: deliver 1st argument
!a!#1!c!d!e!{2}% <- #1 = b: deliver 2nd argument
!a!b!#1!d!e!{3}% <- #1 = c: deliver 3rd argument
!a!b!c!#1!e!{1}% <- #1 = d: deliver 1st argument
!a!b!c!d!#1!{4}% <- #1 = e: deliver 4th argument
!a!b!c!d!e!{5}% <- #1 doesn't contain ! and is neither a nor b nor c nor d nor e: deliver 5th argument
!!!!%
}{5}% <- #1 does contain ! and thus is neither a nor b nor c nor d nor e: deliver 5th argument
%==End of \ExtractKthArg's 1st argument====================================
}{%==\ExtractKthArg's 2nd argument=<tokens in case list of undelimited args
% doesn't have a k-th argumnent> It is left empty as a K-th argument is
% in the list for each value K that can occur.
}{%==\ExtractKthArg's 3rd argument=<list of undelimited arguments>===========
{You typed a or you typed d.}% - 1st argument
{You typed b.}% - 2nd argument
{You typed c.}% - 3rd argument
{You typed e.}% - 4th argument
{You typed something which is neither a nor b nor c nor d nor e.}% - 5th argument
}%
}%
%%=============================================================================
\makeatother
\documentclass{article}
\begin{document}
\dothis{a}
\dothis{b}
\dothis{c}
\dothis{d}
\dothis{e}
\dothis{f}
\dothis{!f!}
\end{document}

If you need to fork many different cases so that a single forking-macro would require a delimiter which is cluttered, you can use more than one forking macro.
E.g., instead of \UD@abcdeFork
you can combine usage of \UD@abcFork
and \UD@deFork
:
\makeatletter
\errorcontextlines=10000
%%=============================================================================
%% PARAPHERNALIA:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond,
%% \UD@stopromannumeral, \UD@CheckWhetherNull,
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
\expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%=============================================================================
%%
%% Extract K-th inner undelimited argument:
%%
%% \ExtractKthArg{<integer K>}%
%% {<tokens in case list of undelimited args doesn't have a k-th argumnent>}%
%% {<list of undelimited args>} %
%%
%% In case there is no K-th argument in <list of indelimited args> :
%% Does deliver <tokens in case list of undelimited args doesn't have a k-th argumnent.
%% In case there is a K-th argument in <list of indelimited args> :
%% Does deliver that K-th argument with one level of braces removed.
%%
%% Examples:
%%
%% \ExtractKthArg{0}{not available}{ABCDE} yields: not available
%%
%% \ExtractKthArg{3}{not available}{ABCDE} yields: C
%%
%% \ExtractKthArg{3}{not available}{AB{CD}E} yields: CD
%%
%% \ExtractKthArg{4}{not available}{{001}{002}{003}{004}{005}} yields: 004
%%
%% \ExtractKthArg{6}{not available}{{001}{002}{003}} yields: not available
%%
%% Due to \romannumeral-expansion the result can be obtained by triggering
%% two expansion-steps.
%%=============================================================================
\newcommand\ExtractKthArg[2]{%
\romannumeral%
% #1: <integer number K>
% #2: <action if there is no K-th argument>
\expandafter\UD@ExtractKthArgCheck
\expandafter{\romannumeral\number\number#1 000}{#2}%
}%
\newcommand\UD@ExtractKthArgCheck[3]{%
\UD@CheckWhetherNull{#1}{\UD@stopromannumeral#2}{% empty
\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}%
}%
}%
\begingroup
\def\UD@ExtractFirstArgLoop#1{%
\endgroup
\@ifdefinable\UD@RemoveTillFrozenrelax{%
\long\def\UD@RemoveTillFrozenrelax##1##2#1{{##1}}%
}%
\newcommand\UD@ExtractKthArgLoop[3]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo##3{}.}{\UD@stopromannumeral##2}{%
\UD@CheckWhetherNull{##1}{%
\UD@ExtractFirstArgLoop{##3#1}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}##3}%
{\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}##1}{##2}}%
}%
}%
}%
}%
\expandafter\expandafter\expandafter\UD@ExtractFirstArgLoop
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\ifnum0=0\fi}%
%% Usage of frozen-\relax as delimiter is for speeding things up by reducing the
%% amount of iterations needed. I chose frozen-\relax because David Carlisle
%% pointed out in <https://tex.stackexchange.com/a/578877>
%% that frozen-\relax cannot be (re)defined in terms of \outer and cannot be
%% affected by \uppercase/\lowercase.
%%
%% \UD@ExtractFirstArg's argument may contain frozen-\relax:
%% The only effect is that internally more iterations are needed for
%% obtaining the result.
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{\expandafter\UD@stopromannumeral\UD@firstoftwo#1{}}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillFrozenrelax#1}}%
}%
%% End of code for \ExtractKthArg.
%%=============================================================================
\@ifdefinable\UD@GobbleToExclam{\long\def\UD@GobbleToExclam#1!{}}%
\@ifdefinable\UD@abcFork{%
\long\def\UD@abcFork#1!a!b!c!#2#3!!!!{#2}%
}%
\@ifdefinable\UD@deFork{%
\long\def\UD@deFork#1!d!e!#2#3!!!!{#2}%
}%
\newcommand\dothis[1]{%
\romannumeral\expandafter\UD@secondoftwo
\ExtractKthArg{%
%==\ExtractKthArg's 1st argument=<integer K>===============================
\expandafter\UD@CheckWhetherNull\expandafter{\UD@GobbleToExclam#1!}{%
\UD@abcFork
!#1!b!c!{1}% <- #1 = a: deliver 1st argument
!a!#1!c!{2}% <- #1 = b: deliver 2nd argument
!a!b!#1!{3}% <- #1 = c: deliver 3rd argument
!a!b!c!{% <- #1 does not contain ! and is neither a nor b nor c / in this case number is delivered by \UD@deFork
\UD@deFork
!#1!e!{1}% <- #1 = d: deliver 1st argument
!d!#1!{4}% <- #1 = e: deliver 4th argument
!d!e!{5}% <- #1 (doesn't contain ! and) is neither (a nor b nor c nor) d nor e: deliver 5th argument
!!!!%
}%
!!!!%
}{5}% <- #1 does contain ! and thus is neither a nor b nor c nor d nor e: deliver 5th argument
%==End of \ExtractKthArg's 1st argument====================================
}{%==\ExtractKthArg's 2nd argument=<tokens in case list of undelimited args
% doesn't have a k-th argumnent> It is left empty as a K-th argument is
% in the list for each value K that can occur.
}{%==\ExtractKthArg's 3rd argument=<list of undelimited arguments>===========
{You typed a or you typed d.}% - 1st argument
{You typed b.}% - 2nd argument
{You typed c.}% - 3rd argument
{You typed e.}% - 4th argument
{You typed something which is neither a nor b nor c nor d nor e.}% - 5th argument
}%
}%
%%=============================================================================
\makeatother
\documentclass{article}
\begin{document}
\dothis{a}
\dothis{b}
\dothis{c}
\dothis{d}
\dothis{e}
\dothis{f}
\dothis{!f!}
\end{document}

\pdfstrcmp
?\str_case:nnF
construct that expl3 makes available through e.g.\usepackage{xparse}
. I was initially put off by the unusual characters in the macro name, but that was a mistake — it just has to be surrounded by\ExplSyntaxOn
,\ExplSyntaxOff
to make that syntax valid.\str_case:nnF
is documented in interface3.pdf.