tags:

views:

40

answers:

1

I get a compile time error with the following relevant code snippet at the line that calls NotifyObservers in the if construct.

public class ExternalSystem<TEmployee, TEventArgs> : ISubject<TEventArgs>
    where TEmployee : Employee
    where TEventArgs : EmployeeEventArgs
{
    protected List<IObserver<TEventArgs>> _observers = null;
    protected List<TEmployee> _employees = null;

    public virtual void AddNewEmployee(TEmployee employee)
    {
        if (_employees.Contains(employee) == false)
        {
            _employees.Add(employee);

            string message = FormatMessage("New {0} hired.", employee);

            if (employee is Executive)
                NotifyObservers(new ExecutiveEventArgs { e = employee, msg = message });
            else if (employee is BuildingSecurity)
                NotifyObservers(new BuildingSecurityEventArgs { e = employee, msg = message });
        }
    }

    public void NotifyObservers(TEventArgs args)
    {
        foreach (IObserver<TEventArgs> observer in _observers)
            observer.EmployeeEventHandler(this, args);
    }
}

The error I receive is:

The best overloaded method match for 'ExternalSystem.NotifyObservers(TEventArgs)' has some invalid arguments. Cannot convert from 'ExecutiveEventArgs' to 'TEventArgs'.

I am compiling this in C# 3.0 using Visual Studio 2008 Express Edition.

For now, I've gotten around the problem by branching out the specific object instantiation into overridden methods, like the snippet given below, but I am need to understand why the error occurred. I thought the compiler could infer the type hierarchy in the above situation.

public class ExternalSystem<TEmployee, TEventArgs> : ISubject<TEventArgs>
    where TEmployee : Employee where TEventArgs: EmployeeEventArgs
{

protected List<IObserver<TEventArgs>> _observers = null;
protected List<TEmployee> _employees = null;

protected virtual void AddNewEmployee(TEmployee employee)
{
    if (_employees.Contains(employee) == false)
    {
        _employees.Add(employee);

        string message = FormatMessage("New {0} hired.", employee);

        NotifyObservers(GetEventArgs(employee, message));
    }
}


protected virtual TEventArgs GetEventArgs(TEmployee employee, string message)
{
    return default(TEventArgs);
}

public void NotifyObservers(TEventArgs args)
{
    foreach (IObserver<TEventArgs> observer in _observers)
        observer.EmployeeEventHandler(this, args);
}
}

public class SecuritySystem : 
ExternalSystem<BuildingSecurity, BuildingSecurityEventArgs>
{
    public SecuritySystem() : base() { }

protected override BuildingSecurityEventArgs GetEventArgs(BuildingSecurity employee, string message)
{
    return new BuildingSecurityEventArgs { msg = message, e = employee };
}

public void HireSecurityGuard(BuildingSecurity buildingSecurity)
{
    this.AddNewEmployee(buildingSecurity);
}

public void FireSecurityGuard(BuildingSecurity buildingSecurity)
{
    this.TerminateEmployee(buildingSecurity);
}

}

+1  A: 

The TEventArgs generic parameter will be of some specific type, which is derived from EmployeeEventArgs, but the compiler doesn't know which exact type it will be until later. The compiler therefore can't allow the conversion, because it doesn't know whether it will be valid.

Just to clarify, if you create an object of type ExternalSystem<Janitor, JanitorEventArgs>, then TEventArgs will be JanitorEventArgs in the context of this object, and both calls to NotifyObservers will be invalid, because ExecutiveEventArgs does not derive from JanitorEventArgs.

Marcelo Cantos
But doesn't the compiler already know that ExecutiveEventArgs is of type EmployeeEventArgs? And so is BuildingSecurityEventArgs?
Water Cooler v2
@WC2, yes, but you are trying to pass it to a method that takes `TEventArgs`, which _isn't_ `EmployeeEventArgs`, but some unspecified type derived from `EmployeeEventArgs`.
Marcelo Cantos