Diğer yanıtlar , isteğe bağlı bir parametrenin neden dinamik bir ifade olamayacağına dair harika açıklamalar verdi. Ancak, anlatmak gerekirse, varsayılan parametreler derleme zamanı sabitleri gibi davranır. Bu, derleyicinin bunları değerlendirip bir cevap bulabilmesi gerektiği anlamına gelir. Sürekli bildirimlerle karşılaşıldığında dinamik ifadeleri değerlendiren derleyiciye C # desteği eklemesini isteyen bazı insanlar var — bu tür bir özellik, yöntemleri "saf" olarak işaretlemeyle ilgili olabilir, ancak bu şu anda bir gerçeklik değildir ve asla olmayabilir.
Böyle bir yöntem için bir C # varsayılan parametresi kullanmanın bir alternatifi, ile örneklenen kalıbı kullanmak olacaktır XmlReaderSettings
. Bu modelde, parametresiz bir kurucuya ve herkese açık olarak yazılabilir özelliklere sahip bir sınıf tanımlayın. Ardından, yönteminizdeki tüm seçenekleri varsayılanlarla bu tür bir nesneyle değiştirin. Hatta null
bunun için bir varsayılan belirterek bu nesneyi isteğe bağlı hale getirin. Örneğin:
public class FooSettings
{
public TimeSpan Span { get; set; } = TimeSpan.FromSeconds(2);
// I imagine that if you had a heavyweight default
// thing you’d want to avoid instantiating it right away
// because the caller might override that parameter. So, be
// lazy! (Or just directly store a factory lambda with Func<IThing>).
Lazy<IThing> thing = new Lazy<IThing>(() => new FatThing());
public IThing Thing
{
get { return thing.Value; }
set { thing = new Lazy<IThing>(() => value); }
}
// Another cool thing about this pattern is that you can
// add additional optional parameters in the future without
// even breaking ABI.
//bool FutureThing { get; set; } = true;
// You can even run very complicated code to populate properties
// if you cannot use a property initialization expression.
//public FooSettings() { }
}
public class Bar
{
public void Foo(FooSettings settings = null)
{
// Allow the caller to use *all* the defaults easily.
settings = settings ?? new FooSettings();
Console.WriteLine(settings.Span);
}
}
Çağrı yapmak için, özellikleri tek bir ifadede örneklemek ve atamak için bu garip sözdizimini kullanın:
bar.Foo(); // 00:00:02
bar.Foo(new FooSettings { Span = TimeSpan.FromDays(1), }); // 1.00:00:00
bar.Foo(new FooSettings { Thing = new MyCustomThing(), }); // 00:00:02
Downsides
Bu, bu sorunu çözmek için gerçekten ağır bir yaklaşımdır. Hızlı ve kirli bir dahili arabirim yazıyorsanız ve null yapılabilir hale getiriyorsanız ve TimeSpan
null'u istediğiniz varsayılan değerin iyi çalışacağı gibi davranıyorsanız , bunun yerine bunu yapın.
Ayrıca, çok sayıda parametreniz varsa veya yöntemi sıkı bir döngüde çağırıyorsanız, bu, sınıf somutlaştırmalarının ek yüküne sahip olacaktır. Elbette, böyle bir yöntemi sıkı bir döngüde çağırıyorsanız, FooSettings
nesnenin bir örneğini yeniden kullanmak doğal ve hatta çok kolay olabilir .
Yararları
Örnekteki yorumda belirttiğim gibi, bu modelin genel API'ler için harika olduğunu düşünüyorum. Bir sınıfa yeni özellikler eklemek, kesintiye uğramayan bir ABI değişikliğidir, bu nedenle bu kalıbı kullanarak yönteminizin imzasını değiştirmeden yeni isteğe bağlı parametreler ekleyebilirsiniz - daha yakın zamanda derlenmiş koda daha fazla seçenek sunarken, eski derlenmiş kodu fazladan çalışma olmadan desteklemeye devam edin .
Ayrıca, C # yerleşik varsayılan yöntem parametreleri derleme süresi sabitleri olarak değerlendirildiğinden ve çağrı sitesinde pişirildiğinden, varsayılan parametreler yalnızca yeniden derlendikten sonra kod tarafından kullanılacaktır. Çağıran, bir ayarlar nesnesinin örneğini oluşturarak, yönteminizi çağırırken varsayılan değerleri dinamik olarak yükler. Bu, yalnızca ayarlar sınıfınızı değiştirerek varsayılanları güncelleyebileceğiniz anlamına gelir. Bu nedenle, bu kalıp, istenirse, arayanları yeni değerleri görmek için yeniden derlemek zorunda kalmadan varsayılan değerleri değiştirmenize olanak tanır.
new TimeSpan(2000)
2000 milisaniye anlamına gelmediğini unutmayın, bu, 0,2 milisaniye veya iki saniyenin 10.000'de biri olan 2000 "tıklama" anlamına gelir.