Jimmyt1988
Jimmyt1988

Reputation: 21126

Telling C# that variable has the properties of an interface

I have the following interface:

public interface ISoftDeletable
{
    bool IsDeleted { get; set; }
}

And my entity:

public class Field : ISoftDeletable
{
    [Key]
    public int FieldID { get; set; }

    public bool IsDeleted { get; set; }
...

In my generic repository i have:

...
if( entity is ISoftDeletable )
{
    entity.IsDeleted = true; // does not know entity is implementing ISoftDeletable, so is throwing error that i cannot set IsDeleted
}
...

but of course, c# does not know entity is implementing ISoftDeletable as entity is generic type.

How can i make C# know the IsDeleted property is okay to set?

Upvotes: 1

Views: 159

Answers (4)

Dave Cousineau
Dave Cousineau

Reputation: 13148

You can use as:

var maybeSoftDeletable = entity as ISoftDeletable;
if (maybeSoftDeletable != null)
   maybeSoftDeletable.IsDeleted = true;

maybeSoftDeletable will be null if entity is not an ISoftDeletable, or otherwise will be an ISoftDeletable reference to the same object.

As per George's answer, you can also cast:

var definitelyASoftDeletable = (ISoftDeletable)entity;
definitelyASoftDeletable.IsDeleted = true;

The main difference is whether you know entity is of the type or not. If entity is allowed to be one or not, then use as. If entity is supposed to be one, then use a cast, which will cause an exception if it violates that rule.

UPDATE: lately I've been using some Maybe and MaybeAs extension methods, which I'm loving so far:

public static void MaybeAs<T>(
   this object pObject,
   Action<T> pAction
) {
   var maybeT = pObject as T;
   if (maybeT == null)
      return;
   var t = maybeT;

   pAction(t);
}

// and one for functions
public static TResult MaybeAs<TObject, TResult>(
   this object pObject,
   Func<TObject, TResult> pFunc
) where TObject : class
where TResult: class {
   var maybeT = pObject as TObject;
   if (maybeT == null)
      return null;
   var t = maybeT;

   return pFunc(t);
}

// to allow for primitive return values
public static TResult MaybeAs<TObject, TResult>(
   this object pObject,
   Func<TObject, TResult> pFunc,
   TResult pValueWhenNot
) where TObject : class {
   var maybeT = pObject as TObject;
   if (maybeT == null)
      return pValueWhenNot;
   var t = maybeT;

   return pFunc(t);
}

Used like:

entity.MaybeAs<ISoftDeletable>(softDeletable => softDeletable.IsDeleted = true);    

Upvotes: 3

erisco
erisco

Reputation: 14329

To my knowledge, the C# type system does not understand that in if (x is X) { (1) } that x has type X or has a subtype of X in (1). This may not even be a safe assumption in C#, I do not know (concurrency?).

Instead, you might write this.

if (entity is ISoftDeletable) {
    ((ISoftDeletable)entity).IsDeleted = true;
}

This has the unfortunate consequence of casting entity twice, once for the branch condition and again to set IsDeleted. The cost of casting can be non-trivial (I'll let you decide for your case). Here is another variation that does not do this.

var deletableEntity = entity as ISoftDeletable;
if (deletableEntity != null) {
    deletableEntity.IsDeleted = true;
}

Upvotes: 4

Erik Philips
Erik Philips

Reputation: 54618

Based on your code I'd slightly change it to:

var softDelete = entity as ISoftDeletable;
if(softDelete != null)
{
  softDelete .IsDeleted = true; 
}

This code is a little more readable about what it is actually doing (IMO).

Upvotes: 6

George Stocker
George Stocker

Reputation: 57872

I don't know anything more about your setup than what you've provided; but this feels fishy to me. It may not be, but I don't have enough of a picture to know.

To solve your immediate issue, you could cast the entity to the ISoftDeletable so that the ISDeleted property can be set:

((ISoftDeletable)entity).IsDeleted = true;

Again; something feels off about having to do that; but it's 9:47pm, so I'm not going to spend too much brain power to figure that out tonight.

Upvotes: 5

Related Questions