-4

Imagine I have a class structure similar to this:

public interface IArbitraryQualifier {
    int Qualification { get; }
}
public class ArbitraryQualifier : IArbitraryQualifier {
    public const int MIN_QUALITY = 1;

    public int Qualification { get; }    
}
public class Person {
    public IArbitraryQualifier ArbitraryQualifier { get; }
    public bool AmIARealBoy 
    { 
      get 
      { 
        return this.ArbitraryQualifier.Qualification >= ArbitraryQualifier.MIN_QUALITY; 
      } 
    }
}

How do I reference the constant field MIN_QUALITY in class ArbitraryQualifier from within class Person? It keeps generating error IDE0009 ("... does not contain a definition for ...") which suggests I "add 'this' or 'me' qualification".

If I rename the property or the concrete class it works just fine, but I don't want to (I am using a boilerplate class named the same as the property).

Note that this also occurs for abstract classes and generic types, but not concrete types. For example, there is no compilation error here:

public class ArbitraryQualifier {
    public const int MIN_QUALITY = 1;

    public int Qualification { get; }    
}
public class Person {
    public ArbitraryQualifier ArbitraryQualifier { get; }
    public bool AmIARealBoy 
    { 
      get 
      { 
        return this.ArbitraryQualifier.Qualification >= ArbitraryQualifier.MIN_QUALITY; 
      }
    }
}

Also, why does this compile when the others do not?

Elaskanator
  • 1,135
  • 10
  • 28
  • 1
    Use the fully qualified name for it (i.e. `Namespace.ClassName.PropertyName`). Using `this` would refer to your local instance. – Rufus L May 13 '19 at 21:41
  • Related: [Defining constants on an interface](https://stackoverflow.com/questions/12752364) (also not a good idea) – Elaskanator May 14 '19 at 13:47

3 Answers3

2

If a class depends on IArbitraryQualifier then it should not know anything about the implementation except that it implements IArbitraryQualifier. That means Person shouldn't know that an implementation of IArbitraryQualifier has a constant named MIN_QUALITY. It should only know what is declared in the interface.

For example, what if we create another implementation of IArbitraryQualifier:

public class OtherQualifier : IArbitraryQualifier 
{
    public int Qualification { get; }  
}

... and then do this:

var person = new Person();
person.ArbitraryQualifier  = new OtherQualifier();

Now there is no MIN_QUALITY constant.

The underlying principle is that 99% of the time we don't want to know anything about an object other than the type as which it is declared. If the property's declared type is IArbitraryQualifier then the members of that interface are all that matter. We don't want to know details about implementations of that interface, like other properties, methods, constants, etc.

A few possible solutions:

  • Put the constant somewhere else, perhaps in the Person class. ArbitraryQualifier doesn't use the constant, so why does it need to contain the constant? (This would be my first choice.) Put the constant in the class that needs it.
  • Change the property type from IArbitraryQualifier to ArbitraryQualifier. If you know that you need to work with that particular class (including its constant) then use that.
  • Least desirable: Add a public property to the IArbitraryQualifier interface so that all implementations have that property. That doesn't seem to make any sense in this case unless there's some reason why implementations of IArbitraryQualifier must provide that value.
Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
1

You can refer to the fully-qualified name of the class if you want to access the static property, which is namespace.classname.propertyname.

For example, the below has namespace Test as part of the FQDN (and it also illustrates another way to write a read-only property using expression-body syntax):

namespace Test
{
    public interface IArbitraryQualifier
    {
        int Qualification { get; }
    }

    public class ArbitraryQualifier : IArbitraryQualifier
    {
        public const int MIN_QUALITY = 1;

        public int Qualification { get; }
    }

    public class Person
    {
        public IArbitraryQualifier ArbitraryQualifier { get; }
        public bool AmIARealBoy => 
            ArbitraryQualifier.Qualification >= Test.ArbitraryQualifier.MIN_QUALITY;
    }
}
Rufus L
  • 36,127
  • 5
  • 30
  • 43
1

While Rufu's Answer resolve the problem, it does not explain why the problem exists.

The problem exists because your property:

public IArbitraryQualifier ArbitraryQualifier { get; }

Is the same name as your concrete class:

public class ArbitraryQualifier

Since the code is contained within the Person class, the compiler will resolve names to (locally=this) Locally-Scoped variables (var... etc), Then Local Fields/Properties, then to Accessible Named classes within the current namespace, then up the namespace chain.

Renaming the property name on the local/this class also solves the problem.

public interface IArbitraryQualifier
{
    int Qualification
    {
        get;
    }
}

public class ArbitraryQualifier : IArbitraryQualifier
{
    public const int MIN_QUALITY = 1;
    public int Qualification
    {
        get;
    }
}

public class Person
{
    public IArbitraryQualifier ArbitraryQualifier2
    {
        get;
    }

    public bool AmIARealBoy
    {
        get
        {
            return this.ArbitraryQualifier2.Qualification >= ArbitraryQualifier.MIN_QUALITY;
        }
    }
}
Erik Philips
  • 53,428
  • 11
  • 128
  • 150