However, if you would go the route of 'if I have included / imported a
header/namespace with solely routines and no classes, I should be able
to call them without specifying their location', you effectively
breaking down the OO nature of C# and will introduce what C++ also has:
a way to program C-style code, without classes.
I call shenanigans on your statement that this is anti-OOP. Consider:
public interface IDataLayer
{
DataSet GetProducts();
...
}
public partial class BusinessLayer <DataLayer>
where DataLayer : IDataLayer
{
}
public partial class BusinessLayer <DataLayer>
{
public class DiscountRules
{
...
}
}
public partial class BusinessLayer <DataLayer>
{
public class Reporting
{
...
}
}
public partial class ShoppingCartUI<DataLayer, LookAndFeel>
where DataLayer : IDataLayer
where LookAndFeel : IPageFormatter
{
static using BusinessLayer<DataLayer>;
public class NavigationMenu
{
delegate void NavButtonHandler(UIManager sender, NavigateEventArgs e);
event NavButtonHandler NavigateViewCart;
event NavButtonHandler NavigateAccountManagement;
event NavButtonHandler NavigateSystemManagement;
// use DiscountRules and Reporting without further qualification
}
public class UIManager
{
public DataLayer GetDataLayer() { ... }
NavigationMenu NavMenu;
}
}
public class LocalDataLayer : IDataLayer
{
// GetProducts()
...
void BackupNow();
void ReplayLogs();
void Vaccuum();
}
public class LocalAdminUI
{
static using BusinessLayer<LocalDataLayer>; // needs reporting logic
static using ShoppingCartUI<LocalDataLayer>;
static void ShowAuthenticationScreen(UIManager sender, NavigateEventArgs
e) { ... }
}
public class Configuration
{
static using BusinessLayer<LocalDataLayer>;
static using ShoppingCartUI<LocalDataLayer>;
void main()
{
UIManager uim = new UIManager();
uim.NavMenu.NavigateSystemManagement +=
LocalAdminUI.ShowAuthenticationScreen;
}
}
Now, although ShoppingCartUI and BusinessLayer don't need to know
implementation details about the data layer,
ShoppingCartUI<>.UIManager.GetDataLayer() passes a strongly-typed
LocalDataLayer which the LocalAdminUI can use to trigger a database backup,
vaccuum, etc. ShoppingCartUI and BusinessLayer would be just as happy with
a ClusteredDataLayer or WebServicesDataLayer, etc.
Ok, this isn't the best example, because the DataLayer is probably a
singleton with a long lifetime and you could just keep a reference to it in
the LocalAdminUI. But what about more classes. Consider the case where an
error event is raised because a database query failed. The handler for that
may need to go well beyond the public interface of the request in order to
perform error analysis, yet getting an adapter class created and subscribed
to the error event for each and every request created isn't necessarily
feasible. Ok you say, just do a runtime cast to get the more detailed
interface. Bad idea. You've lost compile-time static checking -- in the
error handling path, where runtime errors are likely to go unnoticed for the
longest time. Change to a different request class, and the error handler
still happily passes the unit tests against the old mock object, but now
causes a disaster at runtime at the worst possible time -- when you already
had an error you desperately need information about.
And for my system, which has nothing to do with products or shopping carts
or RDBMS, there are quite a few detectable concerns which need to be kept
loosely coupled but all work on the same object tree. A language feature to
import non-instance members into the name search space would be a huge help.