S3060 - exception for interfaces?

In the documentation for S3060/RSPEC-3060 is witten: “… But code that’s specific to a child class should be in that child class, not in the parent.”

If the “child class” is an interface the description does not really apply. I have no idea to work-around the the issue for interfaces. So I recommend to skip interfaces when checking S3060.

Hi @lg2de,

could you please provide a small reproducer for us, to better understand the false positive you are mentioning?

Thanks,
Costin

The following code is from Caliburn.Micro, slightly modified to be compiled in dotnetcore:

void IViewAware.AttachView(object view, object? context)
{
    context ??= DefaultContext;
    this.Views[context] = view;

    var nonGeneratedView = PlatformProvider.Current.GetFirstNonGeneratedView(view);
    if (nonGeneratedView == null)
    {
        return;
    }

    PlatformProvider.Current.ExecuteOnFirstLoad(nonGeneratedView, this.OnViewLoaded);
    this.OnViewAttached(nonGeneratedView, context);
    this.ViewAttached(this, new ViewAttachedEventArgs(nonGeneratedView, context));

    // TODO https://community.sonarsource.com/t/s3060-exception-for-interfaces/56005
    if (!(this is IActivate activatable) || activatable.IsActive)
    {
        PlatformProvider.Current.ExecuteOnLayoutUpdated(nonGeneratedView, this.OnViewReady);
    }
    else
    {
        AttachViewReadyOnActivated(activatable, nonGeneratedView);
    }
}

Hello @lg2de, and thank you for providing us with a code example!

There’s the check if this is implementing an Interface in your example.
I could think of only one scenario on why this is done here, and it’s when this check is in a parent class, and when called from a child, it checks if the child is actually implementing that particular interface.

If this is the case, the part of the documentation you mentioned in your first post also applies here, which means that the logic that applies to the child class that implements IActivate should be in the child itself, not in the parent class.

If this is not the case, could you please provide a generic example?

Thanks,
Mary

Here is our complete, reduced example:

public abstract class ScreenBase
{
  public void AttachToUi(System.Windows.UIElement uiElement)
  {
    // ...
    
    if (this is IActivatable activatable)
    {
      activatable.Activate();
    }
    
    // ...
  }
  
  public void DetachFromUi(System.Windows.UIElement uiElement)
  {
    // ...

    if (this is IDeactivatable deactivatable)
    {
      deactivatable.Deactivate();
    }
    
    // ...
  }
}

public interface IActivatable
{
  void Activate();
}

public interface IDeactivatable
{
  void Deactivate();
}

Why this should be casted to IActivatable?
All implementations of IActivatable shall be “activated”. They should not be responsible to do that.

Hello @lg2de,

I do see why you cannot do otherwise in this particular example.

Still, we don’t consider this to be an FP as this example’s core design is problematic and aligned with the issue that the rule states in its description.
A base class should not change its behavior according to its derived classes and it should not infer its derived classes behavior.

Best Regards,
Mary

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.