Örneğin, bir ICar
arayüz istediğimi ve tüm uygulamaların alanı içerdiğini varsayalım Year
. Bu, her uygulamanın ayrı ayrı beyan etmesi gerektiği anlamına mı geliyor Year
? Bunu arayüzde tanımlamak daha hoş olmaz mıydı?
Örneğin, bir ICar
arayüz istediğimi ve tüm uygulamaların alanı içerdiğini varsayalım Year
. Bu, her uygulamanın ayrı ayrı beyan etmesi gerektiği anlamına mı geliyor Year
? Bunu arayüzde tanımlamak daha hoş olmaz mıydı?
Yanıtlar:
Diğer cevapların çoğu semantik düzeyde doğru olsa da, bu tür sorulara uygulama ayrıntıları seviyesinden de yaklaşmayı ilginç buluyorum.
Bir arayüz, yöntemler içeren bir yuva koleksiyonu olarak düşünülebilir . Bir sınıf bir arabirim uyguladığında, sınıfın çalışma zamanına gerekli tüm yuvaları nasıl dolduracağını bildirmesi gerekir. Dediğinde
interface IFoo { void M(); }
class Foo : IFoo { public void M() { ... } }
"Benim örneğimi oluşturduğunuzda, IFoo.M için yuvadaki Foo.M'ye bir referans girin.
Sonra bir çağrı yaptığınızda:
IFoo ifoo = new Foo();
ifoo.M();
derleyici "nesneye IFoo.M için yuvada hangi yöntemin sormak ve bu yöntemi çağırmak" yazan kod üretir.
Arabirim, yöntemleri içeren bir yuva koleksiyonuysa, bu yuvalardan bazıları bir özelliğin get ve set yöntemlerini, bir dizinleyicinin get ve set yöntemlerini ve bir olayın ekleme ve kaldırma yöntemlerini de içerebilir. Ancak bir alan bir yöntem değildir . Alanla ilişkilendirilmiş bir "yuva" yoktur ve bu alan alanına referansla "doldurabilirsiniz". Bu nedenle arabirimler yöntemleri, özellikleri, dizinleyicileri ve olayları tanımlayabilir, ancak alanları tanımlayamaz.
An interface cannot contain constants, fields, operators
Dan. msdn.microsoft.com/en-us/library/ms173156.aspx )
int One()
, uygulama public int One(){return 1;}
bir alan değildir.
C # 'daki arabirimler, bir sınıfın yapışacağı sözleşmeyi tanımlamayı amaçlamaktadır - belirli bir uygulama değil.
Bu ruh haliyle, C # arabirimleri , çağıranın aşağıdakiler için bir uygulama sağlaması gereken özelliklerin tanımlanmasına izin verir :
interface ICar
{
int Year { get; set; }
}
Özelliklerle ilişkilendirilmiş özel bir mantık yoksa, uygulama sınıfları, uygulamayı basitleştirmek için otomatik özellikleri kullanabilir:
class Automobile : ICar
{
public int Year { get; set; } // automatically implemented
}
Year
?
public int Year => 123;
. Bununla birlikte, bu durumda bir ayarlayıcıya sahip olmanın bir anlamı yoktur, bu nedenle arayüzint Year { get; }
Eric Lippert çivilenmiş, söylediklerini söylemek için farklı bir yol kullanacağım. Bir arabirimin tüm üyeleri sanaldır ve hepsinin arabirimi devralan bir sınıf tarafından geçersiz kılınması gerekir. Sanal anahtar sözcüğü arabirim bildirimine açıkça yazmaz veya sınıfta geçersiz kılma anahtar sözcüğünü kullanmazsınız.
Sanal anahtar kelime yöntemleri ve yöntem işaretçileri bir dizi v-table denilen .NET ile uygulanır. Override anahtar sözcüğü, v-table yuvasını, temel sınıf tarafından üretilenin üzerine yazarak farklı bir yöntem işaretçisiyle doldurur. Özellikler, olaylar ve dizinleyiciler başlık altında yöntem olarak uygulanır. Ancak alanlar değildir. Arayüzler bu nedenle alan içeremez.
Neden sadece Year
gayet iyi bir mülke sahip değilsiniz ?
Alanlar, veri temsili için belirli bir uygulamayı temsil ettiğinden ve bunları ortaya çıkarmak kapsüllemeyi keseceğinden, alanlar içermez. Böylece, bir alanla bir arayüze sahip olmak, bir arayüze sahip olmak için bir uygulama yerine etkili bir şekilde kodlama yapar;
Örneğin, Year
belirtiminizin bir kısmı, ICar
uygulayıcıların Year
geçerli yıl + 1'den veya 1900'den önceki bir süreye atamaya izin vermesinin geçersiz olmasını gerektirebilir . Açıkta kalan Year
alanlarınız varsa - kullanmak çok daha iyi burada iş yapmak yerine özellikleri.
Kısa cevap evet, her uygulama tipinin kendi destek değişkenini yaratması gerekecek. Bunun nedeni, bir arabirimin bir sözleşmeye benzer olmasıdır. Yapabileceği tek şey, bir uygulama türünün kullanıma sunması gereken genel olarak erişilebilir kod parçalarını belirtmektir; herhangi bir kod içeremez.
Önerilerinizi kullanarak bu senaryoyu düşünün:
public interface InterfaceOne
{
int myBackingVariable;
int MyProperty { get { return myBackingVariable; } }
}
public interface InterfaceTwo
{
int myBackingVariable;
int MyProperty { get { return myBackingVariable; } }
}
public class MyClass : InterfaceOne, InterfaceTwo { }
Burada birkaç sorunumuz var:
myBackingVariable
olacak MyClass
kullanabilir?En yaygın yaklaşım, arayüzü ve onu uygulayan bir barebone soyut sınıfını ilan etmektir. Bu, soyut sınıftan miras alma ve uygulamayı ücretsiz alma veya arabirimi açıkça uygulama ve başka bir sınıftan miras alma esnekliğine izin verir. Bunun gibi bir şey çalışır:
public interface IMyInterface
{
int MyProperty { get; set; }
}
public abstract class MyInterfaceBase : IMyInterface
{
int myProperty;
public int MyProperty
{
get { return myProperty; }
set { myProperty = value; }
}
}
Arabirimler herhangi bir uygulama içermez.
Şimdiden çok şey söylendi, ancak basitleştirmek için benim almam. Arayüzler, tüketiciler veya sınıflar tarafından uygulanacak yöntem sözleşmelerine sahip olacak ve değerleri depolayacak alanlara sahip olmayacak şekilde tasarlanmıştır.
O zaman neden özelliklere izin verildiğini iddia edebilirsiniz? Yani basit cevap - özellikler dahili olarak sadece yöntem olarak tanımlanır.
Bunun için yıl alanını uygulayan bir Car temel sınıfına sahip olabilirsiniz ve diğer tüm uygulamalar bundan miras alabilir.
Bir arabirim, genel yönetim ortamı özelliklerini ve yöntemlerini tanımlar . Alanlar tipik olarak özeldir veya en fazla korunan, dahili veya korunan iç kısımdadır ("alan" terimi genellikle herkese açık hiçbir şey için kullanılmaz).
Diğer yanıtlarda belirtildiği gibi, bir temel sınıf tanımlayabilir ve tüm mirasçılar tarafından erişilebilecek korumalı bir özellik tanımlayabilirsiniz.
Bir gariplik, bir arayüzün aslında dahili olarak tanımlanabilmesidir, ancak arayüzün kullanışlılığını sınırlar ve tipik olarak diğer harici kodlar tarafından kullanılmayan dahili işlevselliği tanımlamak için kullanılır.